git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
892
deps/juce/modules/juce_dsp/containers/juce_AudioBlock.h
vendored
Normal file
892
deps/juce/modules/juce_dsp/containers/juce_AudioBlock.h
vendored
Normal file
@@ -0,0 +1,892 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
namespace SampleTypeHelpers // Internal classes needed for handling sample type classes
|
||||
{
|
||||
template <typename T, bool = std::is_floating_point<T>::value>
|
||||
struct ElementType
|
||||
{
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ElementType<T, false>
|
||||
{
|
||||
using Type = typename T::value_type;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Minimal and lightweight data-structure which contains a list of pointers to
|
||||
channels containing some kind of sample data.
|
||||
|
||||
This class doesn't own any of the data which it points to, it's simply a view
|
||||
into data that is owned elsewhere. You can construct one from some raw data
|
||||
that you've allocated yourself, or give it a HeapBlock to use, or give it
|
||||
an AudioBuffer which it can refer to, but in all cases the user is
|
||||
responsible for making sure that the data doesn't get deleted while there's
|
||||
still an AudioBlock using it.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class AudioBlock
|
||||
{
|
||||
private:
|
||||
template <typename OtherSampleType>
|
||||
using MayUseConvertingConstructor =
|
||||
std::enable_if_t<std::is_same<std::remove_const_t<SampleType>,
|
||||
std::remove_const_t<OtherSampleType>>::value
|
||||
&& std::is_const<SampleType>::value
|
||||
&& ! std::is_const<OtherSampleType>::value,
|
||||
int>;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
//==============================================================================
|
||||
/** Create a zero-sized AudioBlock. */
|
||||
AudioBlock() noexcept = default;
|
||||
|
||||
/** Creates an AudioBlock from a pointer to an array of channels.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the memory is retained
|
||||
throughout the life-time of the AudioBlock and released when no longer needed.
|
||||
*/
|
||||
constexpr AudioBlock (SampleType* const* channelData,
|
||||
size_t numberOfChannels, size_t numberOfSamples) noexcept
|
||||
: channels (channelData),
|
||||
numChannels (static_cast<ChannelCountType> (numberOfChannels)),
|
||||
numSamples (numberOfSamples)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock from a pointer to an array of channels.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the memory is retained
|
||||
throughout the life-time of the AudioBlock and released when no longer needed.
|
||||
*/
|
||||
constexpr AudioBlock (SampleType* const* channelData, size_t numberOfChannels,
|
||||
size_t startSampleIndex, size_t numberOfSamples) noexcept
|
||||
: channels (channelData),
|
||||
numChannels (static_cast<ChannelCountType> (numberOfChannels)),
|
||||
startSample (startSampleIndex),
|
||||
numSamples (numberOfSamples)
|
||||
{
|
||||
}
|
||||
|
||||
/** Allocates a suitable amount of space in a HeapBlock, and initialises this object
|
||||
to point into it.
|
||||
The HeapBlock must of course not be freed or re-allocated while this object is still in
|
||||
use, because it will be referencing its data.
|
||||
*/
|
||||
AudioBlock (HeapBlock<char>& heapBlockToUseForAllocation,
|
||||
size_t numberOfChannels, size_t numberOfSamples,
|
||||
size_t alignmentInBytes = defaultAlignment) noexcept
|
||||
: numChannels (static_cast<ChannelCountType> (numberOfChannels)),
|
||||
numSamples (numberOfSamples)
|
||||
{
|
||||
auto roundedUpNumSamples = (numberOfSamples + elementMask) & ~elementMask;
|
||||
auto channelSize = sizeof (SampleType) * roundedUpNumSamples;
|
||||
auto channelListBytes = sizeof (SampleType*) * numberOfChannels;
|
||||
auto extraBytes = alignmentInBytes - 1;
|
||||
|
||||
heapBlockToUseForAllocation.malloc (channelListBytes + extraBytes + channelSize * numberOfChannels);
|
||||
|
||||
auto* chanArray = unalignedPointerCast<SampleType**> (heapBlockToUseForAllocation.getData());
|
||||
channels = chanArray;
|
||||
|
||||
auto* data = unalignedPointerCast<SampleType*> (addBytesToPointer (chanArray, channelListBytes));
|
||||
data = snapPointerToAlignment (data, alignmentInBytes);
|
||||
|
||||
for (ChannelCountType i = 0; i < numChannels; ++i)
|
||||
{
|
||||
chanArray[i] = data;
|
||||
data += roundedUpNumSamples;
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock that points to the data in an AudioBuffer.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the buffer is retained
|
||||
throughout the life-time of the AudioBlock without being modified.
|
||||
*/
|
||||
template <typename OtherSampleType>
|
||||
constexpr AudioBlock (AudioBuffer<OtherSampleType>& buffer) noexcept
|
||||
: channels (buffer.getArrayOfWritePointers()),
|
||||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
|
||||
numSamples (static_cast<size_t> (buffer.getNumSamples()))
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock that points to the data in an AudioBuffer.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the buffer is retained
|
||||
throughout the life-time of the AudioBlock without being modified.
|
||||
*/
|
||||
template <typename OtherSampleType>
|
||||
constexpr AudioBlock (const AudioBuffer<OtherSampleType>& buffer) noexcept
|
||||
: channels (buffer.getArrayOfReadPointers()),
|
||||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
|
||||
numSamples (static_cast<size_t> (buffer.getNumSamples()))
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock that points to the data in an AudioBuffer.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the buffer is retained
|
||||
throughout the life-time of the AudioBlock without being modified.
|
||||
*/
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock (AudioBuffer<OtherSampleType>& buffer, size_t startSampleIndex) noexcept
|
||||
: channels (buffer.getArrayOfWritePointers()),
|
||||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
|
||||
startSample (startSampleIndex),
|
||||
numSamples (static_cast<size_t> (buffer.getNumSamples()) - startSampleIndex)
|
||||
{
|
||||
jassert (startSample < static_cast<size_t> (buffer.getNumSamples()));
|
||||
}
|
||||
|
||||
AudioBlock (const AudioBlock& other) noexcept = default;
|
||||
AudioBlock& operator= (const AudioBlock& other) noexcept = default;
|
||||
|
||||
template <typename OtherSampleType, MayUseConvertingConstructor<OtherSampleType> = 0>
|
||||
AudioBlock (const AudioBlock<OtherSampleType>& other) noexcept
|
||||
: channels { other.channels },
|
||||
numChannels { other.numChannels },
|
||||
startSample { other.startSample },
|
||||
numSamples { other.numSamples }
|
||||
{
|
||||
}
|
||||
|
||||
template <typename OtherSampleType, MayUseConvertingConstructor<OtherSampleType> = 0>
|
||||
AudioBlock& operator= (const AudioBlock<OtherSampleType>& other) noexcept
|
||||
{
|
||||
AudioBlock blockCopy { other };
|
||||
swap (blockCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap (AudioBlock& other) noexcept
|
||||
{
|
||||
std::swap (other.channels, channels);
|
||||
std::swap (other.numChannels, numChannels);
|
||||
std::swap (other.startSample, startSample);
|
||||
std::swap (other.numSamples, numSamples);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename OtherSampleType>
|
||||
constexpr bool operator== (const AudioBlock<OtherSampleType>& other) const noexcept
|
||||
{
|
||||
return std::equal (channels,
|
||||
channels + numChannels,
|
||||
other.channels,
|
||||
other.channels + other.numChannels)
|
||||
&& startSample == other.startSample
|
||||
&& numSamples == other.numSamples;
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
constexpr bool operator!= (const AudioBlock<OtherSampleType>& other) const noexcept
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of channels referenced by this block. */
|
||||
constexpr size_t getNumChannels() const noexcept { return static_cast<size_t> (numChannels); }
|
||||
|
||||
/** Returns the number of samples referenced by this block. */
|
||||
constexpr size_t getNumSamples() const noexcept { return numSamples; }
|
||||
|
||||
/** Returns a raw pointer into one of the channels in this block. */
|
||||
SampleType* getChannelPointer (size_t channel) const noexcept
|
||||
{
|
||||
jassert (channel < numChannels);
|
||||
jassert (numSamples > 0);
|
||||
return channels[channel] + startSample;
|
||||
}
|
||||
|
||||
/** Returns an AudioBlock that represents one of the channels in this block. */
|
||||
AudioBlock getSingleChannelBlock (size_t channel) const noexcept
|
||||
{
|
||||
jassert (channel < numChannels);
|
||||
return AudioBlock (channels + channel, 1, startSample, numSamples);
|
||||
}
|
||||
|
||||
/** Returns a subset of contiguous channels
|
||||
@param channelStart First channel of the subset
|
||||
@param numChannelsToUse Count of channels in the subset
|
||||
*/
|
||||
AudioBlock getSubsetChannelBlock (size_t channelStart, size_t numChannelsToUse) const noexcept
|
||||
{
|
||||
jassert (channelStart < numChannels);
|
||||
jassert ((channelStart + numChannelsToUse) <= numChannels);
|
||||
|
||||
return AudioBlock (channels + channelStart, numChannelsToUse, startSample, numSamples);
|
||||
}
|
||||
|
||||
/** Returns a sample from the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
SampleType getSample (int channel, int sampleIndex) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (isPositiveAndBelow (sampleIndex, numSamples));
|
||||
return channels[channel][(size_t) startSample + (size_t) sampleIndex];
|
||||
}
|
||||
|
||||
/** Modifies a sample in the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
void setSample (int destChannel, int destSample, SampleType newValue) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (isPositiveAndBelow (destSample, numSamples));
|
||||
channels[destChannel][(size_t) startSample + (size_t) destSample] = newValue;
|
||||
}
|
||||
|
||||
/** Adds a value to a sample in the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
void addSample (int destChannel, int destSample, SampleType valueToAdd) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (isPositiveAndBelow (destSample, numSamples));
|
||||
channels[destChannel][(size_t) startSample + (size_t) destSample] += valueToAdd;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the memory referenced by this AudioBlock. */
|
||||
AudioBlock& clear() noexcept { clearInternal(); return *this; }
|
||||
const AudioBlock& clear() const noexcept { clearInternal(); return *this; }
|
||||
|
||||
/** Fills the memory referenced by this AudioBlock with value. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE fill (NumericType value) noexcept { fillInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE fill (NumericType value) const noexcept { fillInternal (value); return *this; }
|
||||
|
||||
/** Copies the values in src to this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& copyFrom (const AudioBlock<OtherSampleType>& src) noexcept { copyFromInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& copyFrom (const AudioBlock<OtherSampleType>& src) const noexcept { copyFromInternal (src); return *this; }
|
||||
|
||||
/** Copy the values from an AudioBuffer to this block.
|
||||
|
||||
All indices and sizes are in this AudioBlock's units, i.e. if SampleType is a
|
||||
SIMDRegister then incrementing srcPos by one will increase the sample position
|
||||
in the AudioBuffer's units by a factor of SIMDRegister<SampleType>::SIMDNumElements.
|
||||
*/
|
||||
template <typename OtherNumericType>
|
||||
AudioBlock& copyFrom (const AudioBuffer<OtherNumericType>& src,
|
||||
size_t srcPos = 0, size_t dstPos = 0,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) { copyFromInternal (src, srcPos, dstPos, numElements); return *this; }
|
||||
template <typename OtherNumericType>
|
||||
const AudioBlock& copyFrom (const AudioBuffer<OtherNumericType>& src,
|
||||
size_t srcPos = 0, size_t dstPos = 0,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const { copyFromInternal (src, srcPos, dstPos, numElements); return *this; }
|
||||
|
||||
|
||||
/** Copies the values from this block to an AudioBuffer.
|
||||
|
||||
All indices and sizes are in this AudioBlock's units, i.e. if SampleType is a
|
||||
SIMDRegister then incrementing dstPos by one will increase the sample position
|
||||
in the AudioBuffer's units by a factor of SIMDRegister<SampleType>::SIMDNumElements.
|
||||
*/
|
||||
void copyTo (AudioBuffer<typename std::remove_const<NumericType>::type>& dst, size_t srcPos = 0, size_t dstPos = 0,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const
|
||||
{
|
||||
auto dstlen = static_cast<size_t> (dst.getNumSamples()) / sizeFactor;
|
||||
auto n = static_cast<int> (jmin (numSamples - srcPos, dstlen - dstPos, numElements) * sizeFactor);
|
||||
auto maxChannels = jmin (static_cast<size_t> (dst.getNumChannels()), static_cast<size_t> (numChannels));
|
||||
|
||||
for (size_t ch = 0; ch < maxChannels; ++ch)
|
||||
FloatVectorOperations::copy (dst.getWritePointer (static_cast<int> (ch),
|
||||
static_cast<int> (dstPos * sizeFactor)),
|
||||
getDataPointer (ch) + (srcPos * sizeFactor),
|
||||
n);
|
||||
}
|
||||
|
||||
/** Move memory within this block from the position srcPos to the position dstPos.
|
||||
If numElements is not specified then move will move the maximum amount of memory.
|
||||
*/
|
||||
AudioBlock& move (size_t srcPos, size_t dstPos,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) noexcept { moveInternal (srcPos, dstPos, numElements); return *this; }
|
||||
const AudioBlock& move (size_t srcPos, size_t dstPos,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const noexcept { moveInternal (srcPos, dstPos, numElements); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Return a new AudioBlock pointing to a sub-block inside this block. This
|
||||
function does not copy the memory and you must ensure that the original memory
|
||||
pointed to by the receiver remains valid through-out the life-time of the
|
||||
returned sub-block.
|
||||
|
||||
@param newOffset The index of an element inside the receiver which will
|
||||
will become the first element of the return value.
|
||||
@param newLength The number of elements of the newly created sub-block.
|
||||
*/
|
||||
AudioBlock getSubBlock (size_t newOffset, size_t newLength) const noexcept
|
||||
{
|
||||
jassert (newOffset < numSamples);
|
||||
jassert (newOffset + newLength <= numSamples);
|
||||
|
||||
return AudioBlock (channels, numChannels, startSample + newOffset, newLength);
|
||||
}
|
||||
|
||||
/** Return a new AudioBlock pointing to a sub-block inside this block. This
|
||||
function does not copy the memory and you must ensure that the original memory
|
||||
pointed to by the receiver remains valid through-out the life-time of the
|
||||
returned sub-block.
|
||||
|
||||
@param newOffset The index of an element inside the block which will
|
||||
will become the first element of the return value.
|
||||
The return value will include all subsequent elements
|
||||
of the receiver.
|
||||
*/
|
||||
AudioBlock getSubBlock (size_t newOffset) const noexcept
|
||||
{
|
||||
return getSubBlock (newOffset, getNumSamples() - newOffset);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a fixed value to the elements in this block. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE add (NumericType value) noexcept { addInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE add (NumericType value) const noexcept { addInternal (value); return *this; }
|
||||
|
||||
/** Adds the elements in the src block to the elements in this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& add (AudioBlock<OtherSampleType> src) noexcept { addInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& add (AudioBlock<OtherSampleType> src) const noexcept { addInternal (src); return *this; }
|
||||
|
||||
/** Adds a fixed value to each source value and replaces the contents of this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithSumOf (AudioBlock<OtherSampleType> src, NumericType value) noexcept { replaceWithSumOfInternal (src, value); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithSumOf (AudioBlock<OtherSampleType> src, NumericType value) const noexcept { replaceWithSumOfInternal (src, value); return *this; }
|
||||
|
||||
/** Adds each source1 value to the corresponding source2 value and replaces the contents of this block. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithSumOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithSumOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithSumOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithSumOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Subtracts a fixed value from the elements in this block. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE subtract (NumericType value) noexcept { subtractInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE subtract (NumericType value) const noexcept { subtractInternal (value); return *this; }
|
||||
|
||||
/** Subtracts the source values from the elements in this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& subtract (AudioBlock<OtherSampleType> src) noexcept { subtractInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& subtract (AudioBlock<OtherSampleType> src) const noexcept { subtractInternal (src); return *this; }
|
||||
|
||||
/** Subtracts a fixed value from each source value and replaces the contents of this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithDifferenceOf (AudioBlock<OtherSampleType> src, NumericType value) noexcept { replaceWithDifferenceOfInternal (src, value); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithDifferenceOf (AudioBlock<OtherSampleType> src, NumericType value) const noexcept { replaceWithDifferenceOfInternal (src, value); return *this; }
|
||||
|
||||
/** Subtracts each source2 value from the corresponding source1 value and replaces the contents of this block. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithDifferenceOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithDifferenceOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithDifferenceOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithDifferenceOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies the elements in this block by a fixed value. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE multiplyBy (NumericType value) noexcept { multiplyByInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE multiplyBy (NumericType value) const noexcept { multiplyByInternal (value); return *this; }
|
||||
|
||||
/** Multiplies the elements in this block by the elements in the src block */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& multiplyBy (AudioBlock<OtherSampleType> src) noexcept { multiplyByInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& multiplyBy (AudioBlock<OtherSampleType> src) const noexcept { multiplyByInternal (src); return *this; }
|
||||
|
||||
/** Replaces the elements in this block with the product of the elements in the source src block and a fixed value. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithProductOf (AudioBlock<OtherSampleType> src, NumericType value) noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithProductOf (AudioBlock<OtherSampleType> src, NumericType value) const noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
|
||||
/** Replaces the elements in this block with the product of the elements in the src1 and scr2 blocks. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithProductOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithProductOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies each channels of this block by a smoothly changing value. */
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
AudioBlock& multiplyBy (SmoothedValue<OtherSampleType, SmoothingType>& value) noexcept { multiplyByInternal (value); return *this; }
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
const AudioBlock& multiplyBy (SmoothedValue<OtherSampleType, SmoothingType>& value) const noexcept { multiplyByInternal (value); return *this; }
|
||||
|
||||
/** Replaces each channel of this block with the product of the src block and a smoothed value. */
|
||||
template <typename BlockSampleType, typename SmootherSampleType, typename SmoothingType>
|
||||
AudioBlock& replaceWithProductOf (AudioBlock<BlockSampleType> src, SmoothedValue<SmootherSampleType, SmoothingType>& value) noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
template <typename BlockSampleType, typename SmootherSampleType, typename SmoothingType>
|
||||
const AudioBlock& replaceWithProductOf (AudioBlock<BlockSampleType> src, SmoothedValue<SmootherSampleType, SmoothingType>& value) const noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies each value in src by a fixed value and adds the result to this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE addProductOf (AudioBlock<OtherSampleType> src, NumericType factor) noexcept { addProductOfInternal (src, factor); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE addProductOf (AudioBlock<OtherSampleType> src, NumericType factor) const noexcept { addProductOfInternal (src, factor); return *this; }
|
||||
|
||||
/** Multiplies each value in srcA with the corresponding value in srcB and adds the result to this block. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& addProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { addProductOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& addProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { addProductOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Negates each value of this block. */
|
||||
AudioBlock& negate() noexcept { negateInternal(); return *this; }
|
||||
const AudioBlock& negate() const noexcept { negateInternal(); return *this; }
|
||||
|
||||
/** Replaces the contents of this block with the negative of the values in the src block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& replaceWithNegativeOf (AudioBlock<OtherSampleType> src) noexcept { replaceWithNegativeOfInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& replaceWithNegativeOf (AudioBlock<OtherSampleType> src) const noexcept { replaceWithNegativeOfInternal (src); return *this; }
|
||||
|
||||
/** Replaces the contents of this block with the absolute values of the src block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& replaceWithAbsoluteValueOf (AudioBlock<OtherSampleType> src) noexcept { replaceWithAbsoluteValueOfInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& replaceWithAbsoluteValueOf (AudioBlock<OtherSampleType> src) const noexcept { replaceWithAbsoluteValueOfInternal (src); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Replaces each element of this block with the minimum of the corresponding element of the source arrays. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithMinOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithMinOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithMinOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithMinOfInternal (src1, src2); return *this; }
|
||||
|
||||
/** Replaces each element of this block with the maximum of the corresponding element of the source arrays. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithMaxOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithMaxOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithMaxOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithMaxOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the minimum and maximum value of the buffer. */
|
||||
Range<typename std::remove_const<NumericType>::type> findMinAndMax() const noexcept
|
||||
{
|
||||
if (numChannels == 0)
|
||||
return {};
|
||||
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
auto minmax = FloatVectorOperations::findMinAndMax (getDataPointer (0), n);
|
||||
|
||||
for (size_t ch = 1; ch < numChannels; ++ch)
|
||||
minmax = minmax.getUnionWith (FloatVectorOperations::findMinAndMax (getDataPointer (ch), n));
|
||||
|
||||
return minmax;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Convenient operator wrappers.
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE operator+= (NumericType value) noexcept { return add (value); }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE operator+= (NumericType value) const noexcept { return add (value); }
|
||||
|
||||
AudioBlock& operator+= (AudioBlock src) noexcept { return add (src); }
|
||||
const AudioBlock& operator+= (AudioBlock src) const noexcept { return add (src); }
|
||||
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE operator-= (NumericType value) noexcept { return subtract (value); }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE operator-= (NumericType value) const noexcept { return subtract (value); }
|
||||
|
||||
AudioBlock& operator-= (AudioBlock src) noexcept { return subtract (src); }
|
||||
const AudioBlock& operator-= (AudioBlock src) const noexcept { return subtract (src); }
|
||||
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE operator*= (NumericType value) noexcept { return multiplyBy (value); }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE operator*= (NumericType value) const noexcept { return multiplyBy (value); }
|
||||
|
||||
AudioBlock& operator*= (AudioBlock src) noexcept { return multiplyBy (src); }
|
||||
const AudioBlock& operator*= (AudioBlock src) const noexcept { return multiplyBy (src); }
|
||||
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
AudioBlock& operator*= (SmoothedValue<OtherSampleType, SmoothingType>& value) noexcept { return multiplyBy (value); }
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
const AudioBlock& operator*= (SmoothedValue<OtherSampleType, SmoothingType>& value) const noexcept { return multiplyBy (value); }
|
||||
|
||||
//==============================================================================
|
||||
// This class can only be used with floating point types
|
||||
static_assert (std::is_same<std::remove_const_t<SampleType>, float>::value
|
||||
|| std::is_same<std::remove_const_t<SampleType>, double>::value
|
||||
#if JUCE_USE_SIMD
|
||||
|| std::is_same<std::remove_const_t<SampleType>, SIMDRegister<float>>::value
|
||||
|| std::is_same<std::remove_const_t<SampleType>, SIMDRegister<double>>::value
|
||||
#endif
|
||||
, "AudioBlock only supports single or double precision floating point types");
|
||||
|
||||
//==============================================================================
|
||||
/** Applies a function to each value in an input block, putting the result into an output block.
|
||||
The function supplied must take a SampleType as its parameter, and return a SampleType.
|
||||
The two blocks must have the same number of channels and samples.
|
||||
*/
|
||||
template <typename Src1SampleType, typename Src2SampleType, typename FunctionType>
|
||||
static void process (AudioBlock<Src1SampleType> inBlock, AudioBlock<Src2SampleType> outBlock, FunctionType&& function)
|
||||
{
|
||||
auto len = inBlock.getNumSamples();
|
||||
auto numChans = inBlock.getNumChannels();
|
||||
|
||||
jassert (len == outBlock.getNumSamples());
|
||||
jassert (numChans == outBlock.getNumChannels());
|
||||
|
||||
for (ChannelCountType c = 0; c < numChans; ++c)
|
||||
{
|
||||
auto* src = inBlock.getChannelPointer (c);
|
||||
auto* dst = outBlock.getChannelPointer (c);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = function (src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NumericType* getDataPointer (size_t channel) const noexcept
|
||||
{
|
||||
return reinterpret_cast<NumericType*> (getChannelPointer (channel));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_VECTOR_CALLTYPE clearInternal() const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::clear (getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
void JUCE_VECTOR_CALLTYPE fillInternal (NumericType value) const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::fill (getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void copyFromInternal (const AudioBlock<OtherSampleType>& src) const noexcept
|
||||
{
|
||||
auto maxChannels = jmin (src.numChannels, numChannels);
|
||||
auto n = static_cast<int> (jmin (src.numSamples * src.sizeFactor,
|
||||
numSamples * sizeFactor));
|
||||
|
||||
for (size_t ch = 0; ch < maxChannels; ++ch)
|
||||
FloatVectorOperations::copy (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherNumericType>
|
||||
void copyFromInternal (const AudioBuffer<OtherNumericType>& src, size_t srcPos, size_t dstPos, size_t numElements) const
|
||||
{
|
||||
auto srclen = static_cast<size_t> (src.getNumSamples()) / sizeFactor;
|
||||
auto n = static_cast<int> (jmin (srclen - srcPos, numSamples - dstPos, numElements) * sizeFactor);
|
||||
auto maxChannels = jmin (static_cast<size_t> (src.getNumChannels()), static_cast<size_t> (numChannels));
|
||||
|
||||
for (size_t ch = 0; ch < maxChannels; ++ch)
|
||||
FloatVectorOperations::copy (getDataPointer (ch) + (dstPos * sizeFactor),
|
||||
src.getReadPointer (static_cast<int> (ch),
|
||||
static_cast<int> (srcPos * sizeFactor)),
|
||||
n);
|
||||
}
|
||||
|
||||
void moveInternal (size_t srcPos, size_t dstPos,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const noexcept
|
||||
{
|
||||
jassert (srcPos <= numSamples && dstPos <= numSamples);
|
||||
auto len = jmin (numSamples - srcPos, numSamples - dstPos, numElements) * sizeof (SampleType);
|
||||
|
||||
if (len != 0)
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
::memmove (getChannelPointer (ch) + dstPos,
|
||||
getChannelPointer (ch) + srcPos, len);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_VECTOR_CALLTYPE addInternal (NumericType value) const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void addInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE replaceWithSumOfInternal (AudioBlock<OtherSampleType> src, NumericType value) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), src.getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithSumOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
constexpr void JUCE_VECTOR_CALLTYPE subtractInternal (NumericType value) const noexcept
|
||||
{
|
||||
addInternal (value * static_cast<NumericType> (-1.0));
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void subtractInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::subtract (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE replaceWithDifferenceOfInternal (AudioBlock<OtherSampleType> src, NumericType value) const noexcept
|
||||
{
|
||||
replaceWithSumOfInternal (src, static_cast<NumericType> (-1.0) * value);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithDifferenceOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::subtract (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_VECTOR_CALLTYPE multiplyByInternal (NumericType value) const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void multiplyByInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE replaceWithProductOfInternal (AudioBlock<OtherSampleType> src, NumericType value) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), src.getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithProductOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
void multiplyByInternal (SmoothedValue<OtherSampleType, SmoothingType>& value) const noexcept
|
||||
{
|
||||
if (! value.isSmoothing())
|
||||
{
|
||||
multiplyByInternal ((NumericType) value.getTargetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
const auto scaler = (NumericType) value.getNextValue();
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
getDataPointer (ch)[i] *= scaler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BlockSampleType, typename SmootherSampleType, typename SmoothingType>
|
||||
void replaceWithProductOfInternal (AudioBlock<BlockSampleType> src, SmoothedValue<SmootherSampleType, SmoothingType>& value) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
|
||||
if (! value.isSmoothing())
|
||||
{
|
||||
replaceWithProductOfInternal (src, (NumericType) value.getTargetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto n = jmin (numSamples, src.numSamples) * sizeFactor;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
const auto scaler = (NumericType) value.getNextValue();
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
getDataPointer (ch)[i] = scaler * src.getChannelPointer (ch)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE addProductOfInternal (AudioBlock<OtherSampleType> src, NumericType factor) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::addWithMultiply (getDataPointer (ch), src.getDataPointer (ch), factor, n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void addProductOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::addWithMultiply (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
constexpr void negateInternal() const noexcept
|
||||
{
|
||||
multiplyByInternal (static_cast<NumericType> (-1.0));
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void replaceWithNegativeOfInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::negate (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void replaceWithAbsoluteValueOfInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::abs (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithMinOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::min (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithMaxOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::max (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
using ChannelCountType = unsigned int;
|
||||
|
||||
//==============================================================================
|
||||
static constexpr size_t sizeFactor = sizeof (SampleType) / sizeof (NumericType);
|
||||
static constexpr size_t elementMask = sizeFactor - 1;
|
||||
static constexpr size_t byteMask = (sizeFactor * sizeof (NumericType)) - 1;
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
static constexpr size_t defaultAlignment = sizeof (SIMDRegister<NumericType>);
|
||||
#else
|
||||
static constexpr size_t defaultAlignment = sizeof (NumericType);
|
||||
#endif
|
||||
|
||||
SampleType* const* channels;
|
||||
ChannelCountType numChannels = 0;
|
||||
size_t startSample = 0, numSamples = 0;
|
||||
|
||||
template <typename OtherSampleType>
|
||||
friend class AudioBlock;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
503
deps/juce/modules/juce_dsp/containers/juce_AudioBlock_test.cpp
vendored
Normal file
503
deps/juce/modules/juce_dsp/containers/juce_AudioBlock_test.cpp
vendored
Normal file
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
template <typename SampleType>
|
||||
class AudioBlockUnitTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
AudioBlockUnitTests()
|
||||
: UnitTest ("AudioBlock", UnitTestCategories::dsp)
|
||||
{
|
||||
for (auto v : { &data, &otherData })
|
||||
for (auto& channel : *v)
|
||||
channel = allocateAlignedMemory (numSamples);
|
||||
|
||||
block = { data.data(), data.size(), (size_t) numSamples };
|
||||
otherBlock = { otherData.data(), otherData.size(), (size_t) numSamples };
|
||||
|
||||
resetBlocks();
|
||||
}
|
||||
|
||||
~AudioBlockUnitTests() override
|
||||
{
|
||||
for (auto v : { &data, &otherData })
|
||||
for (auto channel : *v)
|
||||
deallocateAlignedMemory (channel);
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Equality");
|
||||
{
|
||||
expect (block == block);
|
||||
expect (block != otherBlock);
|
||||
}
|
||||
|
||||
beginTest ("Constructors");
|
||||
{
|
||||
expect (block == AudioBlock<SampleType> (data.data(), data.size(), numSamples));
|
||||
expect (block == AudioBlock<SampleType> (data.data(), data.size(), (size_t) 0, numSamples));
|
||||
expect (block == AudioBlock<SampleType> (block));
|
||||
|
||||
expect (block == AudioBlock<const SampleType> (data.data(), data.size(), numSamples));
|
||||
expect (block == AudioBlock<const SampleType> (data.data(), data.size(), (size_t) 0, numSamples));
|
||||
expect (block == AudioBlock<const SampleType> (block));
|
||||
}
|
||||
|
||||
beginTest ("Swap");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
expect (block != otherBlock);
|
||||
expect (block.getSample (0, 0) == SampleType (1.0));
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (otherBlock.getSample (0, 0) == SampleType (-1.0));
|
||||
expect (otherBlock.getSample (0, 3) == SampleType (-4.0));
|
||||
|
||||
block.swap (otherBlock);
|
||||
|
||||
expect (block != otherBlock);
|
||||
expect (otherBlock.getSample (0, 0) == SampleType (1.0));
|
||||
expect (otherBlock.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (0, 0) == SampleType (-1.0));
|
||||
expect (block.getSample (0, 3) == SampleType (-4.0));
|
||||
|
||||
block.swap (otherBlock);
|
||||
|
||||
expect (block.getSample (0, 0) == SampleType (1.0));
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (otherBlock.getSample (0, 0) == SampleType (-1.0));
|
||||
expect (otherBlock.getSample (0, 3) == SampleType (-4.0));
|
||||
}
|
||||
|
||||
beginTest ("Getters and setters");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
expectEquals ((int) block.getNumChannels(), (int) data.size());
|
||||
expectEquals ((int) block.getNumSamples(), numSamples);
|
||||
|
||||
expect (block.getChannelPointer (0)[2] == SampleType (3.0));
|
||||
block.getChannelPointer (0)[2] = SampleType (999.0);
|
||||
expect (block.getChannelPointer (0)[2] == SampleType (999.0));
|
||||
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (11.0));
|
||||
|
||||
expect (block.getSingleChannelBlock (1).getSample (0, 3) == block.getSample (1, 3));
|
||||
|
||||
expect (block.getSubsetChannelBlock (0, 2).getSample (1, 3) == block.getSample (1, 3));
|
||||
expect (block.getSubsetChannelBlock (1, 1).getSample (0, 3) == block.getSample (1, 3));
|
||||
|
||||
block.setSample (1, 1, SampleType (777.0));
|
||||
expect (block.getSample (1, 1) == SampleType (777.0));
|
||||
|
||||
block.addSample (1, 1, SampleType (1.0));
|
||||
expect (block.getSample (1, 1) == SampleType (778.0));
|
||||
}
|
||||
|
||||
beginTest ("Basic copying");
|
||||
{
|
||||
block.clear();
|
||||
expect (block.getSample (0, 2) == SampleType (0.0));
|
||||
expect (block.getSample (1, 4) == SampleType (0.0));
|
||||
|
||||
block.fill ((NumericType) 456.0);
|
||||
expect (block.getSample (0, 2) == SampleType (456.0));
|
||||
expect (block.getSample (1, 4) == SampleType (456.0));
|
||||
|
||||
block.copyFrom (otherBlock);
|
||||
expect (block != otherBlock);
|
||||
expect (block.getSample (0, 2) == otherBlock.getSample (0, 2));
|
||||
expect (block.getSample (1, 4) == otherBlock.getSample (1, 4));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
SampleType testSample1 = block.getSample (0, 2);
|
||||
SampleType testSample2 = block.getSample (1, 3);
|
||||
expect (testSample1 != block.getSample (0, 4));
|
||||
expect (testSample2 != block.getSample (1, 5));
|
||||
block.move (0, 2);
|
||||
expect (block.getSample (0, 4) == testSample1);
|
||||
expect (block.getSample (1, 5) == testSample2);
|
||||
}
|
||||
|
||||
beginTest ("Addition");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.add ((NumericType) 15.0);
|
||||
expect (block.getSample (0, 4) == SampleType (20.0));
|
||||
expect (block.getSample (1, 4) == SampleType (26.0));
|
||||
|
||||
block.add (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (15.0));
|
||||
expect (block.getSample (1, 4) == SampleType (15.0));
|
||||
|
||||
block.replaceWithSumOf (otherBlock, (NumericType) 9.0);
|
||||
expect (block.getSample (0, 4) == SampleType (4.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-2.0));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
block.replaceWithSumOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (0.0));
|
||||
expect (block.getSample (1, 4) == SampleType (0.0));
|
||||
}
|
||||
|
||||
beginTest ("Subtraction");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.subtract ((NumericType) 15.0);
|
||||
expect (block.getSample (0, 4) == SampleType (-10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-4.0));
|
||||
|
||||
block.subtract (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (7.0));
|
||||
|
||||
block.replaceWithDifferenceOf (otherBlock, (NumericType) 9.0);
|
||||
expect (block.getSample (0, 4) == SampleType (-14.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-20.0));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
block.replaceWithDifferenceOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (22.0));
|
||||
}
|
||||
|
||||
beginTest ("Multiplication");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.multiplyBy ((NumericType) 10.0);
|
||||
expect (block.getSample (0, 4) == SampleType (50.0));
|
||||
expect (block.getSample (1, 4) == SampleType (110.0));
|
||||
|
||||
block.multiplyBy (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-250.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-1210.0));
|
||||
|
||||
block.replaceWithProductOf (otherBlock, (NumericType) 3.0);
|
||||
expect (block.getSample (0, 4) == SampleType (-15.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-33.0));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
block.replaceWithProductOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-25.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-121.0));
|
||||
}
|
||||
|
||||
beginTest ("Multiply add");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.addProductOf (otherBlock, (NumericType) -1.0);
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (22.0));
|
||||
|
||||
block.addProductOf (otherBlock, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (35.0));
|
||||
expect (block.getSample (1, 4) == SampleType (143.0));
|
||||
}
|
||||
|
||||
beginTest ("Negative abs min max");
|
||||
{
|
||||
resetBlocks();
|
||||
otherBlock.negate();
|
||||
|
||||
block.add (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (22.0));
|
||||
|
||||
block.replaceWithNegativeOf (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-11.0));
|
||||
|
||||
block.clear();
|
||||
otherBlock.negate();
|
||||
block.replaceWithAbsoluteValueOf (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (11.0));
|
||||
|
||||
resetBlocks();
|
||||
block.replaceWithMinOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-11.0));
|
||||
|
||||
resetBlocks();
|
||||
block.replaceWithMaxOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (11.0));
|
||||
|
||||
resetBlocks();
|
||||
auto range = block.findMinAndMax();
|
||||
expect (SampleType (range.getStart()) == SampleType (1.0));
|
||||
expect (SampleType (range.getEnd()) == SampleType (12.0));
|
||||
}
|
||||
|
||||
beginTest ("Operators");
|
||||
{
|
||||
resetBlocks();
|
||||
block += (NumericType) 10.0;
|
||||
expect (block.getSample (0, 4) == SampleType (15.0));
|
||||
expect (block.getSample (1, 4) == SampleType (21.0));
|
||||
block += otherBlock;
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (10.0));
|
||||
|
||||
resetBlocks();
|
||||
block -= (NumericType) 10.0;
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (1.0));
|
||||
block -= otherBlock;
|
||||
expect (block.getSample (0, 4) == SampleType (0.0));
|
||||
expect (block.getSample (1, 4) == SampleType (12.0));
|
||||
|
||||
resetBlocks();
|
||||
block *= (NumericType) 10.0;
|
||||
expect (block.getSample (0, 4) == SampleType (50.0));
|
||||
expect (block.getSample (1, 4) == SampleType (110.0));
|
||||
block *= otherBlock;
|
||||
expect (block.getSample (0, 4) == SampleType (-250.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-1210.0));
|
||||
}
|
||||
|
||||
beginTest ("Process");
|
||||
{
|
||||
resetBlocks();
|
||||
AudioBlock<SampleType>::process (block, otherBlock, [] (SampleType x) { return x + (NumericType) 1.0; });
|
||||
expect (otherBlock.getSample (0, 4) == SampleType (6.0));
|
||||
expect (otherBlock.getSample (1, 4) == SampleType (12.0));
|
||||
}
|
||||
|
||||
beginTest ("Copying");
|
||||
{
|
||||
resetBlocks();
|
||||
copyingTests();
|
||||
}
|
||||
|
||||
beginTest ("Smoothing");
|
||||
{
|
||||
resetBlocks();
|
||||
smoothedValueTests();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename T>
|
||||
using ScalarVoid = typename std::enable_if_t < std::is_scalar <T>::value, void>;
|
||||
|
||||
template <typename T>
|
||||
using SIMDVoid = typename std::enable_if_t <! std::is_scalar <T>::value, void>;
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SampleType>
|
||||
ScalarVoid<T> copyingTests()
|
||||
{
|
||||
auto unchangedElement1 = block.getSample (0, 4);
|
||||
auto unchangedElement2 = block.getSample (1, 1);
|
||||
|
||||
AudioBuffer<SampleType> otherBuffer (otherData.data(), (int) otherData.size(), numSamples);
|
||||
|
||||
block.copyFrom (otherBuffer, 1, 2, 2);
|
||||
|
||||
expectEquals (block.getSample (0, 4), unchangedElement1);
|
||||
expectEquals (block.getSample (1, 1), unchangedElement2);
|
||||
expectEquals (block.getSample (0, 2), otherBuffer.getSample (0, 1));
|
||||
expectEquals (block.getSample (1, 3), otherBuffer.getSample (1, 2));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
unchangedElement1 = otherBuffer.getSample (0, 4);
|
||||
unchangedElement2 = otherBuffer.getSample (1, 3);
|
||||
|
||||
block.copyTo (otherBuffer, 2, 1, 2);
|
||||
|
||||
expectEquals (otherBuffer.getSample (0, 4), unchangedElement1);
|
||||
expectEquals (otherBuffer.getSample (1, 3), unchangedElement2);
|
||||
expectEquals (otherBuffer.getSample (0, 1), block.getSample (0, 2));
|
||||
expectEquals (otherBuffer.getSample (1, 2), block.getSample (1, 3));
|
||||
}
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
template <typename T = SampleType>
|
||||
SIMDVoid<T> copyingTests()
|
||||
{
|
||||
auto numSIMDElements = SIMDRegister<NumericType>::SIMDNumElements;
|
||||
AudioBuffer<NumericType> numericData ((int) block.getNumChannels(),
|
||||
(int) (block.getNumSamples() * numSIMDElements));
|
||||
|
||||
for (int c = 0; c < numericData.getNumChannels(); ++c)
|
||||
std::fill_n (numericData.getWritePointer (c), numericData.getNumSamples(), (NumericType) 1.0);
|
||||
|
||||
numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 0.127, (NumericType) 17.3);
|
||||
|
||||
auto lastUnchangedIndexBeforeCopiedRange = (int) ((numSIMDElements * 2) - 1);
|
||||
auto firstUnchangedIndexAfterCopiedRange = (int) ((numSIMDElements * 4) + 1);
|
||||
auto unchangedElement1 = numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange);
|
||||
auto unchangedElement2 = numericData.getSample (1, firstUnchangedIndexAfterCopiedRange);
|
||||
|
||||
block.copyTo (numericData, 1, 2, 2);
|
||||
|
||||
expectEquals (numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange), unchangedElement1);
|
||||
expectEquals (numericData.getSample (1, firstUnchangedIndexAfterCopiedRange), unchangedElement2);
|
||||
expect (SampleType (numericData.getSample (0, 2 * (int) numSIMDElements)) == block.getSample (0, 1));
|
||||
expect (SampleType (numericData.getSample (1, 3 * (int) numSIMDElements)) == block.getSample (1, 2));
|
||||
|
||||
numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 15.1, (NumericType) 0.7);
|
||||
|
||||
auto unchangedSIMDElement1 = block.getSample (0, 1);
|
||||
auto unchangedSIMDElement2 = block.getSample (1, 4);
|
||||
|
||||
block.copyFrom (numericData, 1, 2, 2);
|
||||
|
||||
expect (block.getSample (0, 1) == unchangedSIMDElement1);
|
||||
expect (block.getSample (1, 4) == unchangedSIMDElement2);
|
||||
expectEquals (block.getSample (0, 2).get (0), numericData.getSample (0, (int) numSIMDElements));
|
||||
expectEquals (block.getSample (1, 3).get (0), numericData.getSample (1, (int) (numSIMDElements * 2)));
|
||||
|
||||
if (numSIMDElements > 1)
|
||||
{
|
||||
expectEquals (block.getSample (0, 2).get (1), numericData.getSample (0, (int) (numSIMDElements + 1)));
|
||||
expectEquals (block.getSample (1, 3).get (1), numericData.getSample (1, (int) ((numSIMDElements * 2) + 1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SampleType>
|
||||
ScalarVoid<T> smoothedValueTests()
|
||||
{
|
||||
block.fill ((SampleType) 1.0);
|
||||
SmoothedValue<SampleType> sv { (SampleType) 1.0 };
|
||||
sv.reset (1, 4);
|
||||
sv.setTargetValue ((SampleType) 0.0);
|
||||
|
||||
block.multiplyBy (sv);
|
||||
expect (block.getSample (0, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (1, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (0, 2) > (SampleType) 0.0);
|
||||
expect (block.getSample (1, 2) > (SampleType) 0.0);
|
||||
expectEquals (block.getSample (0, 5), (SampleType) 0.0);
|
||||
expectEquals (block.getSample (1, 5), (SampleType) 0.0);
|
||||
|
||||
sv.setCurrentAndTargetValue (-1.0f);
|
||||
sv.setTargetValue (0.0f);
|
||||
otherBlock.fill (-1.0f);
|
||||
block.replaceWithProductOf (otherBlock, sv);
|
||||
expect (block.getSample (0, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (1, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (0, 2) > (SampleType) 0.0);
|
||||
expect (block.getSample (1, 2) > (SampleType) 0.0);
|
||||
expectEquals (block.getSample (0, 5), (SampleType) 0.0);
|
||||
expectEquals (block.getSample (1, 5), (SampleType) 0.0);
|
||||
}
|
||||
|
||||
template <typename T = SampleType>
|
||||
SIMDVoid<T> smoothedValueTests() {}
|
||||
|
||||
//==============================================================================
|
||||
void resetBlocks()
|
||||
{
|
||||
auto value = SampleType (1.0);
|
||||
|
||||
for (size_t c = 0; c < block.getNumChannels(); ++c)
|
||||
{
|
||||
for (size_t i = 0; i < block.getNumSamples(); ++i)
|
||||
{
|
||||
block.setSample ((int) c, (int) i, value);
|
||||
value += SampleType (1.0);
|
||||
}
|
||||
}
|
||||
|
||||
otherBlock.replaceWithNegativeOf (block);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static SampleType* allocateAlignedMemory (int numSamplesToAllocate)
|
||||
{
|
||||
auto alignmentLowerBound = std::alignment_of<SampleType>::value;
|
||||
#if ! JUCE_WINDOWS
|
||||
alignmentLowerBound = jmax (sizeof (void*), alignmentLowerBound);
|
||||
#endif
|
||||
auto alignmentOrder = std::ceil (std::log2 (alignmentLowerBound));
|
||||
auto requiredAlignment = (size_t) std::pow (2, alignmentOrder);
|
||||
|
||||
auto size = (size_t) numSamplesToAllocate * sizeof (SampleType);
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
auto* memory = _aligned_malloc (size, requiredAlignment);
|
||||
#else
|
||||
void* memory;
|
||||
auto result = posix_memalign (&memory, requiredAlignment, size);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return static_cast<SampleType*> (memory);
|
||||
}
|
||||
|
||||
void deallocateAlignedMemory (void* address)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
_aligned_free (address);
|
||||
#else
|
||||
free (address);
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int numChannels = 2, numSamples = 6;
|
||||
std::array<SampleType*, numChannels> data, otherData;
|
||||
AudioBlock<SampleType> block, otherBlock;
|
||||
};
|
||||
|
||||
static AudioBlockUnitTests<float> audioBlockFloatUnitTests;
|
||||
static AudioBlockUnitTests<double> audioBlockDoubleUnitTests;
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
static AudioBlockUnitTests<SIMDRegister<float>> audioBlockSIMDFloatUnitTests;
|
||||
static AudioBlockUnitTests<SIMDRegister<double>> audioBlockSIMDDoubleUnitTests;
|
||||
#endif
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
242
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction.h
vendored
Normal file
242
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction.h
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename Ret, typename... Args>
|
||||
struct Vtable
|
||||
{
|
||||
using Storage = void*;
|
||||
|
||||
using Move = void (*) (Storage, Storage);
|
||||
using Call = Ret (*) (Storage, Args...);
|
||||
using Clear = void (*) (Storage);
|
||||
|
||||
constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept
|
||||
: move (moveIn), call (callIn), clear (clearIn) {}
|
||||
|
||||
Move move = nullptr;
|
||||
Call call = nullptr;
|
||||
Clear clear = nullptr;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void move (void* from, void* to)
|
||||
{
|
||||
new (to) Fn (std::move (*reinterpret_cast<Fn*> (from)));
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
(*reinterpret_cast<Fn*> (s)) (args...);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<! std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void clear (void* s)
|
||||
{
|
||||
auto& fn = *reinterpret_cast<Fn*> (s);
|
||||
fn.~Fn();
|
||||
// I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
|
||||
ignoreUnused (fn);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
constexpr Vtable<Ret, Args...> makeVtable()
|
||||
{
|
||||
return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <size_t len, typename T>
|
||||
class FixedSizeFunction;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
A type similar to `std::function` that holds a callable object.
|
||||
|
||||
Unlike `std::function`, the callable object will always be stored in
|
||||
a buffer of size `len` that is internal to the FixedSizeFunction instance.
|
||||
This in turn means that creating a FixedSizeFunction instance will never allocate,
|
||||
making FixedSizeFunctions suitable for use in realtime contexts.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <size_t len, typename Ret, typename... Args>
|
||||
class FixedSizeFunction<len, Ret (Args...)>
|
||||
{
|
||||
private:
|
||||
using Storage = typename std::aligned_storage<len>::type;
|
||||
|
||||
template <typename Item>
|
||||
using Decay = typename std::decay<Item>::type;
|
||||
|
||||
template <typename Item, typename Fn = Decay<Item>>
|
||||
using IntIfValidConversion = typename std::enable_if<sizeof (Fn) <= len
|
||||
&& alignof (Fn) <= alignof (Storage)
|
||||
&& ! std::is_same<FixedSizeFunction, Fn>::value,
|
||||
int>::type;
|
||||
|
||||
public:
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction() noexcept = default;
|
||||
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction (std::nullptr_t) noexcept
|
||||
: FixedSizeFunction() {}
|
||||
|
||||
FixedSizeFunction (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Forwards the passed Callable into the internal storage buffer. */
|
||||
template <typename Callable,
|
||||
typename Fn = Decay<Callable>,
|
||||
IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction (Callable&& callable)
|
||||
{
|
||||
static_assert (sizeof (Fn) <= len,
|
||||
"The requested function cannot fit in this FixedSizeFunction");
|
||||
static_assert (alignof (Fn) <= alignof (Storage),
|
||||
"FixedSizeFunction cannot accommodate the requested alignment requirements");
|
||||
|
||||
static constexpr auto vtableForCallable = detail::makeVtable<Fn, Ret, Args...>();
|
||||
vtable = &vtableForCallable;
|
||||
|
||||
auto* ptr = new (&storage) Fn (std::forward<Callable> (callable));
|
||||
jassertquiet ((void*) ptr == (void*) &storage);
|
||||
}
|
||||
|
||||
/** Move constructor. */
|
||||
FixedSizeFunction (FixedSizeFunction&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Converting constructor from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Nulls this instance. */
|
||||
FixedSizeFunction& operator= (std::nullptr_t) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction();
|
||||
}
|
||||
|
||||
FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Assigns a new callable to this instance. */
|
||||
template <typename Callable, IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction& operator= (Callable&& callable)
|
||||
{
|
||||
return *this = FixedSizeFunction (std::forward<Callable> (callable));
|
||||
}
|
||||
|
||||
/** Move assignment from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction (std::move (other));
|
||||
}
|
||||
|
||||
/** Move assignment operator. */
|
||||
FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
|
||||
{
|
||||
clear();
|
||||
vtable = other.vtable;
|
||||
move (std::move (other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~FixedSizeFunction() noexcept { clear(); }
|
||||
|
||||
/** If this instance is currently storing a callable object, calls that object,
|
||||
otherwise throws `std::bad_function_call`.
|
||||
*/
|
||||
Ret operator() (Args... args) const
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
return vtable->call (&storage, std::forward<Args> (args)...);
|
||||
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
/** Returns true if this instance currently holds a callable. */
|
||||
explicit operator bool() const noexcept { return vtable != nullptr; }
|
||||
|
||||
private:
|
||||
template <size_t, typename>
|
||||
friend class FixedSizeFunction;
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->clear (&storage);
|
||||
}
|
||||
|
||||
template <size_t otherLen, typename T>
|
||||
void move (FixedSizeFunction<otherLen, T>&& other) noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->move (&other.storage, &storage);
|
||||
}
|
||||
|
||||
const detail::Vtable<Ret, Args...>* vtable = nullptr;
|
||||
mutable Storage storage;
|
||||
};
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
352
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction_test.cpp
vendored
Normal file
352
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction_test.cpp
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_ENABLE_ALLOCATION_HOOKS
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this)
|
||||
#else
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
class ConstructCounts
|
||||
{
|
||||
auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); }
|
||||
|
||||
public:
|
||||
int constructions = 0;
|
||||
int copies = 0;
|
||||
int moves = 0;
|
||||
int calls = 0;
|
||||
int destructions = 0;
|
||||
|
||||
ConstructCounts withConstructions (int i) const noexcept { auto c = *this; c.constructions = i; return c; }
|
||||
ConstructCounts withCopies (int i) const noexcept { auto c = *this; c.copies = i; return c; }
|
||||
ConstructCounts withMoves (int i) const noexcept { auto c = *this; c.moves = i; return c; }
|
||||
ConstructCounts withCalls (int i) const noexcept { auto c = *this; c.calls = i; return c; }
|
||||
ConstructCounts withDestructions (int i) const noexcept { auto c = *this; c.destructions = i; return c; }
|
||||
|
||||
bool operator== (const ConstructCounts& other) const noexcept { return tie() == other.tie(); }
|
||||
bool operator!= (const ConstructCounts& other) const noexcept { return tie() != other.tie(); }
|
||||
};
|
||||
|
||||
String& operator<< (String& str, const ConstructCounts& c)
|
||||
{
|
||||
return str << "{ constructions: " << c.constructions
|
||||
<< ", copies: " << c.copies
|
||||
<< ", moves: " << c.moves
|
||||
<< ", calls: " << c.calls
|
||||
<< ", destructions: " << c.destructions
|
||||
<< " }";
|
||||
}
|
||||
|
||||
class FixedSizeFunctionTest : public UnitTest
|
||||
{
|
||||
static void toggleBool (bool& b) { b = ! b; }
|
||||
|
||||
struct ConstructCounter
|
||||
{
|
||||
explicit ConstructCounter (ConstructCounts& countsIn)
|
||||
: counts (countsIn) {}
|
||||
|
||||
ConstructCounter (const ConstructCounter& c)
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.copies += 1;
|
||||
}
|
||||
|
||||
ConstructCounter (ConstructCounter&& c) noexcept
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.moves += 1;
|
||||
}
|
||||
|
||||
~ConstructCounter() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
public:
|
||||
FixedSizeFunctionTest()
|
||||
: UnitTest ("Fixed Size Function", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Can be constructed and called from a lambda");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
const auto result = 5;
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return result; };
|
||||
|
||||
const FixedSizeFunction<sizeof (lambda), int()> fn (lambda);
|
||||
const auto out = fn();
|
||||
|
||||
expect (wasCalled);
|
||||
expectEquals (result, out);
|
||||
}
|
||||
|
||||
beginTest ("void fn can be constructed from function with return value");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return 5; };
|
||||
const FixedSizeFunction<sizeof (lambda), void()> fn (lambda);
|
||||
|
||||
fn();
|
||||
expect (wasCalled);
|
||||
}
|
||||
|
||||
beginTest ("Can be constructed and called from a function pointer");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool state = false;
|
||||
|
||||
const FixedSizeFunction<sizeof (void*), void (bool&)> fn (toggleBool);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
|
||||
fn (state);
|
||||
expect (! state);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
}
|
||||
|
||||
beginTest ("Default constructed functions throw if called");
|
||||
{
|
||||
const auto a = FixedSizeFunction<8, void()>();
|
||||
expectThrowsType (a(), std::bad_function_call)
|
||||
|
||||
const auto b = FixedSizeFunction<8, void()> (nullptr);
|
||||
expectThrowsType (b(), std::bad_function_call)
|
||||
}
|
||||
|
||||
beginTest ("Functions can be moved");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (ConstructCounter { counts });
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed
|
||||
|
||||
a();
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
|
||||
|
||||
const auto b = std::move (a);
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
|
||||
}
|
||||
|
||||
beginTest ("Functions are destructed properly");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
const ConstructCounter toCopy { counts };
|
||||
|
||||
{
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (toCopy);
|
||||
expectEquals (counts, ConstructCounts().withCopies (1));
|
||||
}
|
||||
|
||||
expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Avoid destructing functions that fail to construct");
|
||||
{
|
||||
struct BadConstructor
|
||||
{
|
||||
explicit BadConstructor (ConstructCounts& c)
|
||||
: counts (c)
|
||||
{
|
||||
counts.constructions += 1;
|
||||
throw std::runtime_error { "this was meant to happen" };
|
||||
}
|
||||
|
||||
~BadConstructor() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
expectThrowsType ((FixedSizeFunction<sizeof (BadConstructor), void()> (BadConstructor { counts })),
|
||||
std::runtime_error)
|
||||
|
||||
expectEquals (counts, ConstructCounts().withConstructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Equality checks work");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> a;
|
||||
expect (! bool (a));
|
||||
expect (a == nullptr);
|
||||
expect (nullptr == a);
|
||||
expect (! (a != nullptr));
|
||||
expect (! (nullptr != a));
|
||||
|
||||
FixedSizeFunction<8, void()> b ([] {});
|
||||
expect (bool (b));
|
||||
expect (b != nullptr);
|
||||
expect (nullptr != b);
|
||||
expect (! (b == nullptr));
|
||||
expect (! (nullptr == b));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be cleared");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> fn ([] {});
|
||||
expect (bool (fn));
|
||||
|
||||
fn = nullptr;
|
||||
expect (! bool (fn));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be assigned");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<8, void()>;
|
||||
|
||||
int numCallsA = 0;
|
||||
int numCallsB = 0;
|
||||
|
||||
Fn x;
|
||||
Fn y;
|
||||
expect (! bool (x));
|
||||
expect (! bool (y));
|
||||
|
||||
x = [&] { numCallsA += 1; };
|
||||
y = [&] { numCallsB += 1; };
|
||||
expect (bool (x));
|
||||
expect (bool (y));
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 0);
|
||||
|
||||
y();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x = std::move (y);
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions may mutate internal state");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, void()>;
|
||||
|
||||
Fn x;
|
||||
expect (! bool (x));
|
||||
|
||||
int numCalls = 0;
|
||||
x = [&numCalls, counter = 0]() mutable { counter += 1; numCalls = counter; };
|
||||
expect (bool (x));
|
||||
|
||||
expectEquals (numCalls, 0);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions can sink move-only parameters");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, int (std::unique_ptr<int>)>;
|
||||
|
||||
auto value = 5;
|
||||
auto ptr = std::make_unique<int> (value);
|
||||
|
||||
Fn fn = [] (std::unique_ptr<int> p) { return *p; };
|
||||
|
||||
expect (value == fn (std::move (ptr)));
|
||||
}
|
||||
|
||||
beginTest ("Functions be converted from smaller functions");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using SmallFn = FixedSizeFunction<20, void()>;
|
||||
using LargeFn = FixedSizeFunction<21, void()>;
|
||||
|
||||
bool smallCalled = false;
|
||||
bool largeCalled = false;
|
||||
|
||||
SmallFn small = [&smallCalled, a = std::array<char, 8>{}] { smallCalled = true; juce::ignoreUnused (a); };
|
||||
LargeFn large = [&largeCalled, a = std::array<char, 8>{}] { largeCalled = true; juce::ignoreUnused (a); };
|
||||
|
||||
large = std::move (small);
|
||||
|
||||
large();
|
||||
|
||||
expect (smallCalled);
|
||||
expect (! largeCalled);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FixedSizeFunctionTest fixedSizedFunctionTest;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
403
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister.h
vendored
Normal file
403
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister.h
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// This class is needed internally.
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A wrapper around the platform's native SIMD register type.
|
||||
|
||||
This class is only available on SIMD machines. Use JUCE_USE_SIMD to query
|
||||
if SIMD is available for your system.
|
||||
|
||||
SIMDRegister<Type> is a templated class representing the native
|
||||
vectorized version of FloatingType. SIMDRegister supports all numerical
|
||||
primitive types and std:complex<float> and std::complex<double> supports
|
||||
and most operations of the corresponding primitive
|
||||
type. Additionally, SIMDRegister can be accessed like an array to extract
|
||||
the individual elements.
|
||||
|
||||
If you are using SIMDRegister as a pointer, then you must ensure that the
|
||||
memory is sufficiently aligned for SIMD vector operations. Failing to do so
|
||||
will result in crashes or very slow code. Use SIMDRegister::isSIMDAligned
|
||||
to query if a pointer is sufficiently aligned for SIMD vector operations.
|
||||
|
||||
Note that using SIMDRegister without enabling optimizations will result
|
||||
in code with very poor performance.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct SIMDRegister
|
||||
{
|
||||
//==============================================================================
|
||||
/** The type that represents the individual constituents of the SIMD Register */
|
||||
using ElementType = Type;
|
||||
|
||||
/** STL compatible value_type definition (same as ElementType). */
|
||||
using value_type = ElementType;
|
||||
|
||||
/** The corresponding primitive integer type, for example, this will be int32_t
|
||||
if type is a float. */
|
||||
using MaskType = typename SIMDInternal::MaskTypeFor<ElementType>::type;
|
||||
|
||||
//==============================================================================
|
||||
// Here are some types which are needed internally
|
||||
|
||||
/** The native primitive type (used internally). */
|
||||
using PrimitiveType = typename SIMDInternal::PrimitiveType<ElementType>::type;
|
||||
|
||||
/** The native operations for this platform and type combination (used internally) */
|
||||
using NativeOps = SIMDNativeOps<PrimitiveType>;
|
||||
|
||||
/** The native type (used internally). */
|
||||
using vSIMDType = typename NativeOps::vSIMDType;
|
||||
|
||||
/** The corresponding integer SIMDRegister type (used internally). */
|
||||
using vMaskType = SIMDRegister<MaskType>;
|
||||
|
||||
/** The internal native type for the corresponding mask type (used internally). */
|
||||
using vMaskSIMDType = typename vMaskType::vSIMDType;
|
||||
|
||||
/** Wrapper for operations which need to be handled differently for complex
|
||||
and scalar types (used internally). */
|
||||
using CmplxOps = CmplxSIMDOps<ElementType>;
|
||||
|
||||
/** Type which is returned when using the subscript operator. The returned type
|
||||
should be used just like the type ElementType. */
|
||||
struct ElementAccess;
|
||||
|
||||
//==============================================================================
|
||||
/** The size in bytes of this register. */
|
||||
static constexpr size_t SIMDRegisterSize = sizeof (vSIMDType);
|
||||
|
||||
/** The number of elements that this vector can hold. */
|
||||
static constexpr size_t SIMDNumElements = SIMDRegisterSize / sizeof (ElementType);
|
||||
|
||||
vSIMDType value;
|
||||
|
||||
/** Default constructor. */
|
||||
inline SIMDRegister() noexcept = default;
|
||||
|
||||
/** Constructs an object from the native SIMD type. */
|
||||
inline SIMDRegister (vSIMDType a) noexcept : value (a) {}
|
||||
|
||||
/** Constructs an object from a scalar type by broadcasting it to all elements. */
|
||||
inline SIMDRegister (Type s) noexcept { *this = s; }
|
||||
|
||||
/** Destructor. */
|
||||
inline ~SIMDRegister() noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of elements in this vector. */
|
||||
static constexpr size_t size() noexcept { return SIMDNumElements; }
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a new SIMDRegister from the corresponding scalar primitive.
|
||||
The scalar is extended to all elements of the vector. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE expand (ElementType s) noexcept { return {CmplxOps::expand (s)}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the internal SIMD type (for example
|
||||
__mm128 for single-precision floating point on SSE architectures). */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {a}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the first SIMDNumElements of a scalar array. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray (const ElementType* a) noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
return {CmplxOps::load (a)};
|
||||
}
|
||||
|
||||
/** Copies the elements of the SIMDRegister to a scalar array in memory. */
|
||||
inline void JUCE_VECTOR_CALLTYPE copyToRawArray (ElementType* a) const noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
CmplxOps::store (value, a);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE get (size_t idx) const noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return CmplxOps::get (value, idx);
|
||||
}
|
||||
|
||||
/** Sets the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline void JUCE_VECTOR_CALLTYPE set (size_t idx, ElementType v) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
value = CmplxOps::set (value, idx, v);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE operator[] (size_t idx) const noexcept
|
||||
{
|
||||
return get (idx);
|
||||
}
|
||||
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementAccess JUCE_VECTOR_CALLTYPE operator[] (size_t idx) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return ElementAccess (*this, idx);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (SIMDRegister v) noexcept { value = NativeOps::add (value, v.value); return *this; }
|
||||
|
||||
/** Subtracts another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (SIMDRegister v) noexcept { value = NativeOps::sub (value, v.value); return *this; }
|
||||
|
||||
/** Multiplies another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (SIMDRegister v) noexcept { value = CmplxOps::mul (value, v.value); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Broadcasts the scalar to all elements of the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator= (ElementType s) noexcept { value = CmplxOps::expand (s); return *this; }
|
||||
|
||||
/** Adds a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (ElementType s) noexcept { value = NativeOps::add (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Subtracts a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (ElementType s) noexcept { value = NativeOps::sub (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Multiplies a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (ElementType s) noexcept { value = CmplxOps::mul (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (vMaskType v) noexcept { value = NativeOps::bit_and (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-or the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (vMaskType v) noexcept { value = NativeOps::bit_or (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-xor the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (vMaskType v) noexcept { value = NativeOps::bit_xor (value, toVecType (v.value)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (MaskType s) noexcept { value = NativeOps::bit_and (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-or each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (MaskType s) noexcept { value = NativeOps::bit_or (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-xor each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (MaskType s) noexcept { value = NativeOps::bit_xor (value, toVecType (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the sum of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (SIMDRegister v) const noexcept { return { NativeOps::add (value, v.value) }; }
|
||||
|
||||
/** Returns the difference of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (SIMDRegister v) const noexcept { return { NativeOps::sub (value, v.value) }; }
|
||||
|
||||
/** Returns the product of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (SIMDRegister v) const noexcept { return { CmplxOps::mul (value, v.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the sum of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (ElementType s) const noexcept { return { NativeOps::add (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the difference of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (ElementType s) const noexcept { return { NativeOps::sub (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the product of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (ElementType s) const noexcept { return { CmplxOps::mul (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the bit-and of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (vMaskType v) const noexcept { return { NativeOps::bit_and (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-or of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (vMaskType v) const noexcept { return { NativeOps::bit_or (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-xor of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (vMaskType v) const noexcept { return { NativeOps::bit_xor (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-inverted value of the corresponding element in the receiver.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator~() const noexcept { return { NativeOps::bit_not (value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the bit-and'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (MaskType s) const noexcept { return { NativeOps::bit_and (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-or'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (MaskType s) const noexcept { return { NativeOps::bit_or (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-xor'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (MaskType s) const noexcept { return { NativeOps::bit_xor (value, toVecType (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if all element-wise comparisons return true. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (SIMDRegister other) const noexcept { return NativeOps::allEqual (value, other.value); }
|
||||
|
||||
/** Returns true if any element-wise comparisons return false. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (SIMDRegister other) const noexcept { return ! (*this == other); }
|
||||
|
||||
/** Returns true if all elements are equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (Type s) const noexcept { return *this == SIMDRegister::expand (s); }
|
||||
|
||||
/** Returns true if any elements are not equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (Type s) const noexcept { return ! (*this == s); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE equal (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::equal (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is not equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE notEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::notEqual (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is less than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (a.value, b.value)); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a new vector where each element is the minimum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE min (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::min (a.value, b.value) }; }
|
||||
|
||||
/** Returns a new vector where each element is the maximum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies b and c and adds the result to a. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd (SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept
|
||||
{
|
||||
return { CmplxOps::muladd (a.value, b.value, c.value) };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a scalar which is the sum of all elements of the receiver. */
|
||||
inline ElementType sum() const noexcept { return CmplxOps::sum (value); }
|
||||
|
||||
//==============================================================================
|
||||
/** Truncates each element to its integer part.
|
||||
Effectively discards the fractional part of each element. A.k.a. round to zero. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE truncate (SIMDRegister a) noexcept { return { NativeOps::truncate (a.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the absolute value of each element. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE abs (SIMDRegister a) noexcept
|
||||
{
|
||||
return a - (a * (expand (ElementType (2)) & lessThan (a, expand (ElementType (0)))));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Checks if the given pointer is sufficiently aligned for using SIMD operations. */
|
||||
static bool isSIMDAligned (const ElementType* ptr) noexcept
|
||||
{
|
||||
uintptr_t bitmask = SIMDRegisterSize - 1;
|
||||
return (reinterpret_cast<uintptr_t> (ptr) & bitmask) == 0;
|
||||
}
|
||||
|
||||
/** Returns the next position in memory where isSIMDAligned returns true.
|
||||
|
||||
If the current position in memory is already aligned then this method
|
||||
will simply return the pointer.
|
||||
*/
|
||||
static ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept
|
||||
{
|
||||
return snapPointerToAlignment (ptr, SIMDRegisterSize);
|
||||
}
|
||||
|
||||
private:
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vSIMDType in;
|
||||
vMaskSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return vMaskType::fromNative (u.out);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return u.out;
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = CmplxSIMDOps<MaskType>::expand (a);
|
||||
return u.out;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#include "juce_SIMDRegister_Impl.h"
|
||||
#endif
|
||||
177
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h
vendored
Normal file
177
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
struct SIMDRegister<Type>::ElementAccess
|
||||
{
|
||||
operator Type() const { return simd.get (idx); }
|
||||
ElementAccess& operator= (Type scalar) noexcept { simd.set (idx, scalar); return *this; }
|
||||
ElementAccess& operator= (ElementAccess& o) noexcept { return operator= ((Type) o); }
|
||||
|
||||
private:
|
||||
friend struct SIMDRegister;
|
||||
ElementAccess (SIMDRegister& owner, size_t index) noexcept : simd (owner), idx (index) {}
|
||||
SIMDRegister& simd;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
//==============================================================================
|
||||
/* This class is used internally by SIMDRegister to abstract away differences
|
||||
in operations which are different for complex and pure floating point types. */
|
||||
|
||||
// the pure floating-point version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const Scalar* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (a);
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, Scalar* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, dest);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::expand (s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::get (v, i);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::set (v, i, s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::sum (a);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::mul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::multiplyAdd (a, b, c);
|
||||
}
|
||||
};
|
||||
|
||||
// The pure complex version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps<std::complex<Scalar>>
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const std::complex<Scalar>* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (reinterpret_cast<const Scalar*> (a));
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, std::complex<Scalar>* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, reinterpret_cast<Scalar*> (dest));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex<Scalar> s) noexcept
|
||||
{
|
||||
const int n = sizeof (vSIMDType) / sizeof (Scalar);
|
||||
|
||||
union
|
||||
{
|
||||
vSIMDType v;
|
||||
Scalar floats[(size_t) n];
|
||||
} u;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
u.floats[i] = (i & 1) == 0 ? s.real() : s.imag();
|
||||
|
||||
return u.v;
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return std::complex<Scalar> (SIMDNativeOps<Scalar>::get (v, j), SIMDNativeOps<Scalar>::get (v, j + 1));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, std::complex<Scalar> s) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return SIMDNativeOps<Scalar>::set (SIMDNativeOps<Scalar>::set (v, j, s.real()), j + 1, s.imag());
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
vSIMDType result = SIMDNativeOps<Scalar>::oddevensum (a);
|
||||
auto* ptr = reinterpret_cast<const Scalar*> (&result);
|
||||
return std::complex<Scalar> (ptr[0], ptr[1]);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::cmplxmul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::add (a, SIMDNativeOps<Scalar>::cmplxmul (b, c));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace util
|
||||
{
|
||||
template <typename Type>
|
||||
inline void snapToZero (SIMDRegister<Type>&) noexcept {}
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
|
||||
// Extend some common used global functions to SIMDRegister types
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmin (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::min (a, b); }
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmax (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::max (a, b); }
|
||||
|
||||
} // namespace juce
|
||||
918
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp
vendored
Normal file
918
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp
vendored
Normal file
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
namespace SIMDRegister_test_internal
|
||||
{
|
||||
template <typename type, typename = void> struct RandomPrimitive {};
|
||||
|
||||
template <typename type>
|
||||
struct RandomPrimitive<type, typename std::enable_if<std::is_floating_point<type>::value>::type>
|
||||
{
|
||||
static type next (Random& random)
|
||||
{
|
||||
return static_cast<type> (std::is_signed<type>::value ? (random.nextFloat() * 16.0) - 8.0
|
||||
: (random.nextFloat() * 8.0));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template <typename type>
|
||||
struct RandomPrimitive<type, typename std::enable_if<std::is_integral<type>::value>::type>
|
||||
{
|
||||
static type next (Random& random)
|
||||
{
|
||||
return static_cast<type> (random.nextInt64());
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template <typename type> struct RandomValue { static type next (Random& random) { return RandomPrimitive<type>::next (random); } };
|
||||
template <typename type>
|
||||
struct RandomValue<std::complex<type>>
|
||||
{
|
||||
static std::complex<type> next (Random& random)
|
||||
{
|
||||
return {RandomPrimitive<type>::next (random), RandomPrimitive<type>::next (random)};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename type>
|
||||
struct VecFiller
|
||||
{
|
||||
static void fill (type* dst, const int size, Random& random)
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
dst[i] = RandomValue<type>::next (random);
|
||||
}
|
||||
};
|
||||
|
||||
// We need to specialise for complex types: otherwise GCC 6 gives
|
||||
// us an ICE internal compiler error after which the compiler seg faults.
|
||||
template <typename type>
|
||||
struct VecFiller<std::complex<type>>
|
||||
{
|
||||
static void fill (std::complex<type>* dst, const int size, Random& random)
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
dst[i] = std::complex<type> (RandomValue<type>::next (random), RandomValue<type>::next (random));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename type>
|
||||
struct VecFiller<SIMDRegister<type>>
|
||||
{
|
||||
static SIMDRegister<type> fill (Random& random)
|
||||
{
|
||||
constexpr int size = (int) SIMDRegister<type>::SIMDNumElements;
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[size];
|
||||
#else
|
||||
type elements[(size_t) size] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
|
||||
VecFiller<type>::fill (elements, size, random);
|
||||
return SIMDRegister<type>::fromRawArray (elements);
|
||||
}
|
||||
};
|
||||
|
||||
// Avoid visual studio warning
|
||||
template <typename type>
|
||||
static type safeAbs (type a)
|
||||
{
|
||||
return static_cast<type> (std::abs (static_cast<double> (a)));
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static type safeAbs (std::complex<type> a)
|
||||
{
|
||||
return std::abs (a);
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static double difference (type a)
|
||||
{
|
||||
return static_cast<double> (safeAbs (a));
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static double difference (type a, type b)
|
||||
{
|
||||
return difference (a - b);
|
||||
}
|
||||
}
|
||||
|
||||
// These tests need to be strictly run on all platforms supported by JUCE as the
|
||||
// SIMD code is highly platform dependent.
|
||||
|
||||
class SIMDRegisterUnitTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
SIMDRegisterUnitTests()
|
||||
: UnitTest ("SIMDRegister UnitTests", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
// Some helper classes
|
||||
template <typename type>
|
||||
static bool allValuesEqualTo (const SIMDRegister<type>& vec, const type scalar)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
|
||||
#else
|
||||
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
|
||||
vec.copyToRawArray (elements);
|
||||
|
||||
// as we do not want to rely on the access operator we cast this to a primitive pointer
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
if (elements[i] != scalar) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static bool vecEqualToArray (const SIMDRegister<type>& vec, const type* array)
|
||||
{
|
||||
HeapBlock<type> vecElementsStorage (SIMDRegister<type>::SIMDNumElements * 2);
|
||||
auto* ptr = SIMDRegister<type>::getNextSIMDAlignedPtr (vecElementsStorage.getData());
|
||||
vec.copyToRawArray (ptr);
|
||||
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
{
|
||||
double delta = SIMDRegister_test_internal::difference (ptr[i], array[i]);
|
||||
if (delta > 1e-4)
|
||||
{
|
||||
DBG ("a: " << SIMDRegister_test_internal::difference (ptr[i]) << " b: " << SIMDRegister_test_internal::difference (array[i]) << " difference: " << delta);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static void copy (SIMDRegister<type>& vec, const type* ptr)
|
||||
{
|
||||
if (SIMDRegister<type>::isSIMDAligned (ptr))
|
||||
{
|
||||
vec = SIMDRegister<type>::fromRawArray (ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
vec[i] = ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Some useful operations to test
|
||||
struct Addition
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a += b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
|
||||
struct Subtraction
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a -= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
|
||||
struct Multiplication
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a *= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitAND
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a &= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a & b;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitOR
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a |= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a | b;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitXOR
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a ^= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a ^ b;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// the individual tests
|
||||
struct InitializationTest
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
u.expect (allValuesEqualTo<type> (SIMDRegister<type>::expand (static_cast<type> (23)), 23));
|
||||
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
|
||||
#else
|
||||
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (elements, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister<type> a (SIMDRegister<type>::fromRawArray (elements));
|
||||
|
||||
u.expect (vecEqualToArray (a, elements));
|
||||
|
||||
SIMDRegister<type> b (a);
|
||||
a *= static_cast<type> (2);
|
||||
|
||||
u.expect (vecEqualToArray (b, elements));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AccessTest
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
// set-up
|
||||
SIMDRegister<type> a;
|
||||
type array [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
// Test non-const access operator
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
a[i] = array[i];
|
||||
|
||||
u.expect (vecEqualToArray (a, array));
|
||||
|
||||
// Test const access operator
|
||||
const SIMDRegister<type>& b = a;
|
||||
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
u.expect (b[i] == array[i]);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Operation>
|
||||
struct OperatorTests
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
for (int n = 0; n < 100; ++n)
|
||||
{
|
||||
// set-up
|
||||
SIMDRegister<type> a (static_cast<type> (0));
|
||||
SIMDRegister<type> b (static_cast<type> (0));
|
||||
SIMDRegister<type> c (static_cast<type> (0));
|
||||
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_c [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
copy (a, array_a); copy (b, array_b); copy (c, array_c);
|
||||
|
||||
// test in-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<type, type> (array_a[i], array_b[i]);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
copy (a, array_a); copy (b, array_b); copy (c, array_c);
|
||||
|
||||
// test in-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<type, type> (array_b[i], static_cast<type> (2));
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, type> (b, 2);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
// set-up again
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a, array_a); copy (b, array_b); copy (c, array_c);
|
||||
|
||||
// test out-of-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
array_c[i] = Operation::template outofplace<type, type> (array_a[i], array_b[i]);
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, array_c));
|
||||
|
||||
// test out-of-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
array_c[i] = Operation::template outofplace<type, type> (array_b[i], static_cast<type> (2));
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, type> (b, 2);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, array_c));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Operation>
|
||||
struct BitOperatorTests
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
typedef typename SIMDRegister<type>::vMaskType vMaskType;
|
||||
typedef typename SIMDRegister<type>::MaskType MaskType;
|
||||
|
||||
for (int n = 0; n < 100; ++n)
|
||||
{
|
||||
// Check flip sign bit and using as a union
|
||||
{
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
union ConversionUnion
|
||||
{
|
||||
inline ConversionUnion() : floatVersion (static_cast<type> (0)) {}
|
||||
inline ~ConversionUnion() {}
|
||||
SIMDRegister<type> floatVersion;
|
||||
vMaskType intVersion;
|
||||
} a, b;
|
||||
|
||||
vMaskType bitmask = vMaskType::expand (static_cast<MaskType> (1) << (sizeof (MaskType) - 1));
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a.floatVersion, array_a);
|
||||
copy (b.floatVersion, array_a);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, vMaskType> (a.floatVersion, bitmask);
|
||||
Operation::template inplace<vMaskType, vMaskType> (b.intVersion, bitmask);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
|
||||
#else
|
||||
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
b.floatVersion.copyToRawArray (elements);
|
||||
|
||||
u.expect (vecEqualToArray (a.floatVersion, elements));
|
||||
}
|
||||
|
||||
// set-up
|
||||
SIMDRegister<type> a, c;
|
||||
vMaskType b;
|
||||
|
||||
MaskType array_a [SIMDRegister<MaskType>::SIMDNumElements];
|
||||
MaskType array_b [SIMDRegister<MaskType>::SIMDNumElements];
|
||||
MaskType array_c [SIMDRegister<MaskType>::SIMDNumElements];
|
||||
|
||||
type float_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type float_c [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<MaskType>::fill (array_b, SIMDRegister<MaskType>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
copy (a, float_a); copy (b, array_b); copy (c, float_c);
|
||||
|
||||
// test in-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<MaskType, MaskType> (array_a[i], array_b[i]);
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, vMaskType> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<MaskType>::fill (array_b, SIMDRegister<MaskType>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
copy (a, float_a); copy (b, array_b); copy (c, float_c);
|
||||
|
||||
// test in-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
// set-up again
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<MaskType>::fill (array_b, SIMDRegister<MaskType>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
copy (a, float_a); copy (b, array_b); copy (c, float_c);
|
||||
|
||||
// test out-of-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
{
|
||||
array_c[i] =
|
||||
Operation::template outofplace<MaskType, MaskType> (array_a[i], array_b[i]);
|
||||
}
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, vMaskType> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, float_c));
|
||||
|
||||
// test out-of-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
array_c[i] = Operation::template outofplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, float_c));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckComparisonOps
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
typedef typename SIMDRegister<type>::vMaskType vMaskType;
|
||||
typedef typename SIMDRegister<type>::MaskType MaskType;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
// set-up
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_eq [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_neq [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_lt [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_le [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_gt [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_ge [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
// do check
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
array_eq [j] = (array_a[j] == array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_neq [j] = (array_a[j] != array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_lt [j] = (array_a[j] < array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_le [j] = (array_a[j] <= array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_gt [j] = (array_a[j] > array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_ge [j] = (array_a[j] >= array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
}
|
||||
|
||||
SIMDRegister<type> a (static_cast<type> (0));
|
||||
SIMDRegister<type> b (static_cast<type> (0));
|
||||
|
||||
vMaskType eq, neq, lt, le, gt, ge;
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
|
||||
eq = SIMDRegister<type>::equal (a, b);
|
||||
neq = SIMDRegister<type>::notEqual (a, b);
|
||||
lt = SIMDRegister<type>::lessThan (a, b);
|
||||
le = SIMDRegister<type>::lessThanOrEqual (a, b);
|
||||
gt = SIMDRegister<type>::greaterThan (a, b);
|
||||
ge = SIMDRegister<type>::greaterThanOrEqual (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (eq, array_eq ));
|
||||
u.expect (vecEqualToArray (neq, array_neq));
|
||||
u.expect (vecEqualToArray (lt, array_lt ));
|
||||
u.expect (vecEqualToArray (le, array_le ));
|
||||
u.expect (vecEqualToArray (gt, array_gt ));
|
||||
u.expect (vecEqualToArray (ge, array_ge ));
|
||||
|
||||
do
|
||||
{
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
} while (std::equal (array_a, array_a + SIMDRegister<type>::SIMDNumElements, array_b));
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
u.expect (a != b);
|
||||
u.expect (b != a);
|
||||
u.expect (! (a == b));
|
||||
u.expect (! (b == a));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a, array_a);
|
||||
copy (b, array_a);
|
||||
|
||||
u.expect (a == b);
|
||||
u.expect (b == a);
|
||||
u.expect (! (a != b));
|
||||
u.expect (! (b != a));
|
||||
|
||||
type scalar = a[0];
|
||||
a = SIMDRegister<type>::expand (scalar);
|
||||
|
||||
u.expect (a == scalar);
|
||||
u.expect (! (a != scalar));
|
||||
|
||||
scalar--;
|
||||
|
||||
u.expect (a != scalar);
|
||||
u.expect (! (a == scalar));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckMultiplyAdd
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
// set-up
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_c [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_d [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_d, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
// check
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
array_d[i] = array_a[i] + (array_b[i] * array_c[i]);
|
||||
|
||||
SIMDRegister<type> a, b, c, d;
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
copy (c, array_c);
|
||||
|
||||
d = SIMDRegister<type>::multiplyAdd (a, b, c);
|
||||
|
||||
u.expect (vecEqualToArray (d, array_d));
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckMinMax
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_min [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_max [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
array_a[j] = static_cast<type> (random.nextInt (127));
|
||||
array_b[j] = static_cast<type> (random.nextInt (127));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
array_min[j] = (array_a[j] < array_b[j]) ? array_a[j] : array_b[j];
|
||||
array_max[j] = (array_a[j] > array_b[j]) ? array_a[j] : array_b[j];
|
||||
}
|
||||
|
||||
SIMDRegister<type> a (static_cast<type> (0));
|
||||
SIMDRegister<type> b (static_cast<type> (0));
|
||||
SIMDRegister<type> vMin (static_cast<type> (0));
|
||||
SIMDRegister<type> vMax (static_cast<type> (0));
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
|
||||
vMin = jmin (a, b);
|
||||
vMax = jmax (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (vMin, array_min));
|
||||
u.expect (vecEqualToArray (vMax, array_max));
|
||||
|
||||
copy (vMin, array_a);
|
||||
copy (vMax, array_a);
|
||||
|
||||
vMin = SIMDRegister<type>::min (a, b);
|
||||
vMax = SIMDRegister<type>::max (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (vMin, array_min));
|
||||
u.expect (vecEqualToArray (vMax, array_max));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckSum
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
type array [SIMDRegister<type>::SIMDNumElements];
|
||||
type sumCheck = 0;
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
sumCheck += array[j];
|
||||
}
|
||||
|
||||
SIMDRegister<type> a;
|
||||
copy (a, array);
|
||||
|
||||
u.expect (SIMDRegister_test_internal::difference (sumCheck, a.sum()) < 1e-4);
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckAbs
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
type inArray[SIMDRegister<type>::SIMDNumElements];
|
||||
type outArray[SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (inArray, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
SIMDRegister<type> a;
|
||||
copy (a, inArray);
|
||||
a = SIMDRegister<type>::abs (a);
|
||||
|
||||
auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : type (-x); };
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
outArray[j] = calcAbs (inArray[j]);
|
||||
|
||||
u.expect (vecEqualToArray (a, outArray));
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckTruncate
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
type inArray[SIMDRegister<type>::SIMDNumElements];
|
||||
type outArray[SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (inArray, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
SIMDRegister<type> a;
|
||||
copy (a, inArray);
|
||||
a = SIMDRegister<type>::truncate (a);
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
outArray[j] = (type) (int) inArray[j];
|
||||
|
||||
u.expect (vecEqualToArray (a, outArray));
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckBoolEquals
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
bool is_signed = std::is_signed<type>::value;
|
||||
type array [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
auto value = is_signed ? static_cast<type> ((random.nextFloat() * 16.0) - 8.0)
|
||||
: static_cast<type> (random.nextFloat() * 8.0);
|
||||
|
||||
std::fill (array, array + SIMDRegister<type>::SIMDNumElements, value);
|
||||
SIMDRegister<type> a, b;
|
||||
copy (a, array);
|
||||
|
||||
u.expect (a == value);
|
||||
u.expect (! (a != value));
|
||||
value += 1;
|
||||
|
||||
u.expect (a != value);
|
||||
u.expect (! (a == value));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a, array);
|
||||
copy (b, array);
|
||||
|
||||
u.expect (a == b);
|
||||
u.expect (! (a != b));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (b, array);
|
||||
|
||||
u.expect (a != b);
|
||||
u.expect (! (a == b));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <class TheTest>
|
||||
void runTestFloatingPoint (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <class TheTest>
|
||||
void runTestForAllTypes (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
TheTest::template run<int8_t> (*this, random);
|
||||
TheTest::template run<uint8_t> (*this, random);
|
||||
TheTest::template run<int16_t> (*this, random);
|
||||
TheTest::template run<uint16_t>(*this, random);
|
||||
TheTest::template run<int32_t> (*this, random);
|
||||
TheTest::template run<uint32_t>(*this, random);
|
||||
TheTest::template run<int64_t> (*this, random);
|
||||
TheTest::template run<uint64_t>(*this, random);
|
||||
TheTest::template run<std::complex<float>> (*this, random);
|
||||
TheTest::template run<std::complex<double>> (*this, random);
|
||||
}
|
||||
|
||||
template <class TheTest>
|
||||
void runTestNonComplex (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
TheTest::template run<int8_t> (*this, random);
|
||||
TheTest::template run<uint8_t> (*this, random);
|
||||
TheTest::template run<int16_t> (*this, random);
|
||||
TheTest::template run<uint16_t>(*this, random);
|
||||
TheTest::template run<int32_t> (*this, random);
|
||||
TheTest::template run<uint32_t>(*this, random);
|
||||
TheTest::template run<int64_t> (*this, random);
|
||||
TheTest::template run<uint64_t>(*this, random);
|
||||
}
|
||||
|
||||
template <class TheTest>
|
||||
void runTestSigned (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
TheTest::template run<int8_t> (*this, random);
|
||||
TheTest::template run<int16_t> (*this, random);
|
||||
TheTest::template run<int32_t> (*this, random);
|
||||
TheTest::template run<int64_t> (*this, random);
|
||||
}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
runTestForAllTypes<InitializationTest> ("InitializationTest");
|
||||
|
||||
runTestForAllTypes<AccessTest> ("AccessTest");
|
||||
|
||||
runTestForAllTypes<OperatorTests<Addition>> ("AdditionOperators");
|
||||
runTestForAllTypes<OperatorTests<Subtraction>> ("SubtractionOperators");
|
||||
runTestForAllTypes<OperatorTests<Multiplication>> ("MultiplicationOperators");
|
||||
|
||||
runTestForAllTypes<BitOperatorTests<BitAND>> ("BitANDOperators");
|
||||
runTestForAllTypes<BitOperatorTests<BitOR>> ("BitOROperators");
|
||||
runTestForAllTypes<BitOperatorTests<BitXOR>> ("BitXOROperators");
|
||||
|
||||
runTestNonComplex<CheckComparisonOps> ("CheckComparisons");
|
||||
runTestNonComplex<CheckBoolEquals> ("CheckBoolEquals");
|
||||
runTestNonComplex<CheckMinMax> ("CheckMinMax");
|
||||
|
||||
runTestForAllTypes<CheckMultiplyAdd> ("CheckMultiplyAdd");
|
||||
runTestForAllTypes<CheckSum> ("CheckSum");
|
||||
|
||||
runTestSigned<CheckAbs> ("CheckAbs");
|
||||
|
||||
runTestFloatingPoint<CheckTruncate> ("CheckTruncate");
|
||||
}
|
||||
};
|
||||
|
||||
static SIMDRegisterUnitTests SIMDRegisterUnitTests;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
702
deps/juce/modules/juce_dsp/filter_design/juce_FilterDesign.cpp
vendored
Normal file
702
deps/juce/modules/juce_dsp/filter_design/juce_FilterDesign.cpp
vendored
Normal file
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 FloatType>
|
||||
typename FIR::Coefficients<FloatType>::Ptr
|
||||
FilterDesign<FloatType>::designFIRLowpassWindowMethod (FloatType frequency,
|
||||
double sampleRate, size_t order,
|
||||
WindowingMethod type,
|
||||
FloatType beta)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
|
||||
auto* result = new typename FIR::Coefficients<FloatType> (order + 1u);
|
||||
|
||||
auto* c = result->getRawCoefficients();
|
||||
auto normalisedFrequency = frequency / sampleRate;
|
||||
|
||||
for (size_t i = 0; i <= order; ++i)
|
||||
{
|
||||
if (i == order / 2)
|
||||
{
|
||||
c[i] = static_cast<FloatType> (normalisedFrequency * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto indice = MathConstants<double>::pi * (static_cast<double> (i) - 0.5 * static_cast<double> (order));
|
||||
c[i] = static_cast<FloatType> (std::sin (2.0 * indice * normalisedFrequency) / indice);
|
||||
}
|
||||
}
|
||||
|
||||
WindowingFunction<FloatType> theWindow (order + 1, type, false, beta);
|
||||
theWindow.multiplyWithWindowingTable (c, order + 1);
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
typename FIR::Coefficients<FloatType>::Ptr
|
||||
FilterDesign<FloatType>::designFIRLowpassKaiserMethod (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType amplitudedB)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
jassert (normalisedTransitionWidth > 0 && normalisedTransitionWidth <= 0.5);
|
||||
jassert (amplitudedB >= -100 && amplitudedB <= 0);
|
||||
|
||||
FloatType beta = 0;
|
||||
|
||||
if (amplitudedB < -50)
|
||||
beta = static_cast<FloatType> (0.1102 * (-amplitudedB - 8.7));
|
||||
else if (amplitudedB <= -21)
|
||||
beta = static_cast<FloatType> (0.5842 * std::pow (-amplitudedB - 21, 0.4) + 0.07886 * (-amplitudedB - 21));
|
||||
|
||||
int order = amplitudedB < -21 ? roundToInt (std::ceil ((-amplitudedB - 7.95) / (2.285 * normalisedTransitionWidth * MathConstants<double>::twoPi)))
|
||||
: roundToInt (std::ceil (5.79 / (normalisedTransitionWidth * MathConstants<double>::twoPi)));
|
||||
|
||||
jassert (order >= 0);
|
||||
|
||||
return designFIRLowpassWindowMethod (frequency, sampleRate, static_cast<size_t> (order),
|
||||
WindowingFunction<FloatType>::kaiser, beta);
|
||||
}
|
||||
|
||||
|
||||
template <typename FloatType>
|
||||
typename FIR::Coefficients<FloatType>::Ptr
|
||||
FilterDesign<FloatType>::designFIRLowpassTransitionMethod (FloatType frequency, double sampleRate, size_t order,
|
||||
FloatType normalisedTransitionWidth, FloatType spline)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
jassert (normalisedTransitionWidth > 0 && normalisedTransitionWidth <= 0.5);
|
||||
jassert (spline >= 1.0 && spline <= 4.0);
|
||||
|
||||
auto normalisedFrequency = frequency / static_cast<FloatType> (sampleRate);
|
||||
|
||||
auto* result = new typename FIR::Coefficients<FloatType> (order + 1u);
|
||||
auto* c = result->getRawCoefficients();
|
||||
|
||||
for (size_t i = 0; i <= order; ++i)
|
||||
{
|
||||
if (i == order / 2 && order % 2 == 0)
|
||||
{
|
||||
c[i] = static_cast<FloatType> (2 * normalisedFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto indice = MathConstants<double>::pi * ((double) i - 0.5 * (double) order);
|
||||
auto indice2 = MathConstants<double>::pi * normalisedTransitionWidth * ((double) i - 0.5 * (double) order) / spline;
|
||||
c[i] = static_cast<FloatType> (std::sin (2 * indice * normalisedFrequency)
|
||||
/ indice * std::pow (std::sin (indice2) / indice2, spline));
|
||||
}
|
||||
}
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
typename FIR::Coefficients<FloatType>::Ptr
|
||||
FilterDesign<FloatType>::designFIRLowpassLeastSquaresMethod (FloatType frequency,
|
||||
double sampleRate, size_t order,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType stopBandWeight)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
jassert (normalisedTransitionWidth > 0 && normalisedTransitionWidth <= 0.5);
|
||||
jassert (stopBandWeight >= 1.0 && stopBandWeight <= 100.0);
|
||||
|
||||
auto normalisedFrequency = static_cast<double> (frequency) / sampleRate;
|
||||
|
||||
auto wp = MathConstants<double>::twoPi * (static_cast<double> (normalisedFrequency - normalisedTransitionWidth / 2.0));
|
||||
auto ws = MathConstants<double>::twoPi * (static_cast<double> (normalisedFrequency + normalisedTransitionWidth / 2.0));
|
||||
|
||||
auto N = order + 1;
|
||||
|
||||
auto* result = new typename FIR::Coefficients<FloatType> (static_cast<size_t> (N));
|
||||
auto* c = result->getRawCoefficients();
|
||||
|
||||
if (N % 2 == 1)
|
||||
{
|
||||
// Type I
|
||||
auto M = (N - 1) / 2;
|
||||
|
||||
Matrix<double> b (M + 1, 1),
|
||||
q (2 * M + 1, 1);
|
||||
|
||||
auto sinc = [] (double x) { return x == 0 ? 1 : std::sin (x * MathConstants<double>::pi)
|
||||
/ (MathConstants<double>::pi * x); };
|
||||
|
||||
auto factorp = wp / MathConstants<double>::pi;
|
||||
auto factors = ws / MathConstants<double>::pi;
|
||||
|
||||
for (size_t i = 0; i <= M; ++i)
|
||||
b (i, 0) = factorp * sinc (factorp * (double) i);
|
||||
|
||||
q (0, 0) = factorp + stopBandWeight * (1.0 - factors);
|
||||
|
||||
for (size_t i = 1; i <= 2 * M; ++i)
|
||||
q (i, 0) = factorp * sinc (factorp * (double) i) - stopBandWeight * factors * sinc (factors * (double) i);
|
||||
|
||||
auto Q1 = Matrix<double>::toeplitz (q, M + 1);
|
||||
auto Q2 = Matrix<double>::hankel (q, M + 1, 0);
|
||||
|
||||
Q1 += Q2; Q1 *= 0.5;
|
||||
|
||||
Q1.solve (b);
|
||||
|
||||
c[M] = static_cast<FloatType> (b (0, 0));
|
||||
|
||||
for (size_t i = 1; i <= M; ++i)
|
||||
{
|
||||
c[M - i] = static_cast<FloatType> (b (i, 0) * 0.5);
|
||||
c[M + i] = static_cast<FloatType> (b (i, 0) * 0.5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Type II
|
||||
auto M = N / 2;
|
||||
|
||||
Matrix<double> b (M, 1);
|
||||
Matrix<double> qp (2 * M, 1);
|
||||
Matrix<double> qs (2 * M, 1);
|
||||
|
||||
auto sinc = [] (double x) { return x == 0 ? 1 : std::sin (x * MathConstants<double>::pi)
|
||||
/ (MathConstants<double>::pi * x); };
|
||||
|
||||
auto factorp = wp / MathConstants<double>::pi;
|
||||
auto factors = ws / MathConstants<double>::pi;
|
||||
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
b (i, 0) = factorp * sinc (factorp * ((double) i + 0.5));
|
||||
|
||||
for (size_t i = 0; i < 2 * M; ++i)
|
||||
{
|
||||
qp (i, 0) = 0.25 * factorp * sinc (factorp * (double) i);
|
||||
qs (i, 0) = -0.25 * stopBandWeight * factors * sinc (factors * (double) i);
|
||||
}
|
||||
|
||||
auto Q1p = Matrix<double>::toeplitz (qp, M);
|
||||
auto Q2p = Matrix<double>::hankel (qp, M, 1);
|
||||
auto Q1s = Matrix<double>::toeplitz (qs, M);
|
||||
auto Q2s = Matrix<double>::hankel (qs, M, 1);
|
||||
|
||||
auto Id = Matrix<double>::identity (M);
|
||||
Id *= (0.25 * stopBandWeight);
|
||||
|
||||
Q1p += Q2p;
|
||||
Q1s += Q2s;
|
||||
Q1s += Id;
|
||||
|
||||
auto& Q = Q1s;
|
||||
Q += Q1p;
|
||||
|
||||
Q.solve (b);
|
||||
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
{
|
||||
c[M - i - 1] = static_cast<FloatType> (b (i, 0) * 0.25);
|
||||
c[M + i] = static_cast<FloatType> (b (i, 0) * 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
typename FIR::Coefficients<FloatType>::Ptr
|
||||
FilterDesign<FloatType>::designFIRLowpassHalfBandEquirippleMethod (FloatType normalisedTransitionWidth,
|
||||
FloatType amplitudedB)
|
||||
{
|
||||
jassert (normalisedTransitionWidth > 0 && normalisedTransitionWidth <= 0.5);
|
||||
jassert (amplitudedB >= -300 && amplitudedB <= -10);
|
||||
|
||||
auto wpT = (0.5 - normalisedTransitionWidth) * MathConstants<double>::pi;
|
||||
|
||||
auto n = roundToInt (std::ceil ((amplitudedB - 18.18840664 * wpT + 33.64775300) / (18.54155181 * wpT - 29.13196871)));
|
||||
auto kp = (n * wpT - 1.57111377 * n + 0.00665857) / (-1.01927560 * n + 0.37221484);
|
||||
auto A = (0.01525753 * n + 0.03682344 + 9.24760314 / (double) n) * kp + 1.01701407 + 0.73512298 / (double) n;
|
||||
auto B = (0.00233667 * n - 1.35418408 + 5.75145813 / (double) n) * kp + 1.02999650 - 0.72759508 / (double) n;
|
||||
|
||||
auto hn = FilterDesign<FloatType>::getPartialImpulseResponseHn (n, kp);
|
||||
auto hnm = FilterDesign<FloatType>::getPartialImpulseResponseHn (n - 1, kp);
|
||||
|
||||
auto diff = (hn.size() - hnm.size()) / 2;
|
||||
|
||||
for (int i = 0; i < diff; ++i)
|
||||
{
|
||||
hnm.add (0.0);
|
||||
hnm.insert (0, 0.0);
|
||||
}
|
||||
|
||||
auto hh = hn;
|
||||
|
||||
for (int i = 0; i < hn.size(); ++i)
|
||||
hh.setUnchecked (i, A * hh[i] + B * hnm[i]);
|
||||
|
||||
auto* result = new typename FIR::Coefficients<FloatType> (static_cast<size_t> (hh.size()));
|
||||
auto* c = result->getRawCoefficients();
|
||||
|
||||
for (int i = 0; i < hh.size(); ++i)
|
||||
c[i] = (float) hh[i];
|
||||
|
||||
auto NN = [&]
|
||||
{
|
||||
if (n % 2 == 0)
|
||||
return 2.0 * result->getMagnitudeForFrequency (0.5, 1.0);
|
||||
|
||||
auto w01 = std::sqrt (kp * kp + (1 - kp * kp) * std::pow (std::cos (MathConstants<double>::pi / (2.0 * n + 1.0)), 2.0));
|
||||
|
||||
if (std::abs (w01) > 1.0)
|
||||
return 2.0 * result->getMagnitudeForFrequency (0.5, 1.0);
|
||||
|
||||
auto om01 = std::acos (-w01);
|
||||
return -2.0 * result->getMagnitudeForFrequency (om01 / MathConstants<double>::twoPi, 1.0);
|
||||
}();
|
||||
|
||||
for (int i = 0; i < hh.size(); ++i)
|
||||
c[i] = static_cast<FloatType> ((A * hn[i] + B * hnm[i]) / NN);
|
||||
|
||||
c[2 * n + 1] = static_cast<FloatType> (0.5);
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
Array<double> FilterDesign<FloatType>::getPartialImpulseResponseHn (int n, double kp)
|
||||
{
|
||||
Array<double> alpha;
|
||||
alpha.resize (2 * n + 1);
|
||||
|
||||
alpha.setUnchecked (2 * n, 1.0 / std::pow (1.0 - kp * kp, n));
|
||||
|
||||
if (n > 0)
|
||||
alpha.setUnchecked (2 * n - 2, -(2 * n * kp * kp + 1) * alpha[2 * n]);
|
||||
|
||||
if (n > 1)
|
||||
alpha.setUnchecked (2 * n - 4, -(4 * n + 1 + (n - 1) * (2 * n - 1) * kp * kp) / (2.0 * n) * alpha[2 * n - 2]
|
||||
- (2 * n + 1) * ((n + 1) * kp * kp + 1) / (2.0 * n) * alpha[2 * n]);
|
||||
|
||||
for (int k = n; k >= 3; --k)
|
||||
{
|
||||
auto c1 = (3 * (n*(n + 2) - k * (k - 2)) + 2 * k - 3 + 2 * (k - 2)*(2 * k - 3) * kp * kp) * alpha[2 * k - 4];
|
||||
auto c2 = (3 * (n*(n + 2) - (k - 1) * (k + 1)) + 2 * (2 * k - 1) + 2 * k*(2 * k - 1) * kp * kp) * alpha[2 * k - 2];
|
||||
auto c3 = (n * (n + 2) - (k - 1) * (k + 1)) * alpha[2 * k];
|
||||
auto c4 = (n * (n + 2) - (k - 3) * (k - 1));
|
||||
|
||||
alpha.setUnchecked (2 * k - 6, -(c1 + c2 + c3) / c4);
|
||||
}
|
||||
|
||||
Array<double> ai;
|
||||
ai.resize (2 * n + 1 + 1);
|
||||
|
||||
for (int k = 0; k <= n; ++k)
|
||||
ai.setUnchecked (2 * k + 1, alpha[2 * k] / (2.0 * k + 1.0));
|
||||
|
||||
Array<double> hn;
|
||||
hn.resize (2 * n + 1 + 2 * n + 1 + 1);
|
||||
|
||||
for (int k = 0; k <= n; ++k)
|
||||
{
|
||||
hn.setUnchecked (2 * n + 1 + (2 * k + 1), 0.5 * ai[2 * k + 1]);
|
||||
hn.setUnchecked (2 * n + 1 - (2 * k + 1), 0.5 * ai[2 * k + 1]);
|
||||
}
|
||||
|
||||
return hn;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRLowpassHighOrderButterworthMethod (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB)
|
||||
{
|
||||
return designIIRLowpassHighOrderGeneralMethod (0, frequency, sampleRate, normalisedTransitionWidth,
|
||||
passbandAmplitudedB, stopbandAmplitudedB);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRLowpassHighOrderChebyshev1Method (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB)
|
||||
{
|
||||
return designIIRLowpassHighOrderGeneralMethod (1, frequency, sampleRate, normalisedTransitionWidth,
|
||||
passbandAmplitudedB, stopbandAmplitudedB);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRLowpassHighOrderChebyshev2Method (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB)
|
||||
{
|
||||
return designIIRLowpassHighOrderGeneralMethod (2, frequency, sampleRate, normalisedTransitionWidth,
|
||||
passbandAmplitudedB, stopbandAmplitudedB);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRLowpassHighOrderEllipticMethod (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB)
|
||||
{
|
||||
return designIIRLowpassHighOrderGeneralMethod (3, frequency, sampleRate, normalisedTransitionWidth,
|
||||
passbandAmplitudedB, stopbandAmplitudedB);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRLowpassHighOrderGeneralMethod (int type, FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB)
|
||||
{
|
||||
jassert (0 < sampleRate);
|
||||
jassert (0 < frequency && frequency <= sampleRate * 0.5);
|
||||
jassert (0 < normalisedTransitionWidth && normalisedTransitionWidth <= 0.5);
|
||||
jassert (-20 < passbandAmplitudedB && passbandAmplitudedB < 0);
|
||||
jassert (-300 < stopbandAmplitudedB && stopbandAmplitudedB < -20);
|
||||
|
||||
auto normalisedFrequency = frequency / sampleRate;
|
||||
|
||||
auto fp = normalisedFrequency - normalisedTransitionWidth / 2;
|
||||
jassert (0.0 < fp && fp < 0.5);
|
||||
|
||||
auto fs = normalisedFrequency + normalisedTransitionWidth / 2;
|
||||
jassert (0.0 < fs && fs < 0.5);
|
||||
|
||||
double Ap = passbandAmplitudedB;
|
||||
double As = stopbandAmplitudedB;
|
||||
auto Gp = Decibels::decibelsToGain (Ap, -300.0);
|
||||
auto Gs = Decibels::decibelsToGain (As, -300.0);
|
||||
auto epsp = std::sqrt (1.0 / (Gp * Gp) - 1.0);
|
||||
auto epss = std::sqrt (1.0 / (Gs * Gs) - 1.0);
|
||||
|
||||
auto omegap = std::tan (MathConstants<double>::pi * fp);
|
||||
auto omegas = std::tan (MathConstants<double>::pi * fs);
|
||||
constexpr auto halfPi = MathConstants<double>::halfPi;
|
||||
|
||||
auto k = omegap / omegas;
|
||||
auto k1 = epsp / epss;
|
||||
|
||||
int N;
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
N = roundToInt (std::ceil (std::log (1.0 / k1) / std::log (1.0 / k)));
|
||||
}
|
||||
else if (type == 1 || type == 2)
|
||||
{
|
||||
N = roundToInt (std::ceil (std::acosh (1.0 / k1) / std::acosh (1.0 / k)));
|
||||
}
|
||||
else
|
||||
{
|
||||
double K, Kp, K1, K1p;
|
||||
|
||||
SpecialFunctions::ellipticIntegralK (k, K, Kp);
|
||||
SpecialFunctions::ellipticIntegralK (k1, K1, K1p);
|
||||
|
||||
N = roundToInt (std::ceil ((K1p * K) / (K1 * Kp)));
|
||||
}
|
||||
|
||||
const int r = N % 2;
|
||||
const int L = (N - r) / 2;
|
||||
const double H0 = (type == 1 || type == 3) ? std::pow (Gp, 1.0 - r) : 1.0;
|
||||
|
||||
Array<Complex<double>> pa, za;
|
||||
Complex<double> j (0, 1);
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
if (r == 1)
|
||||
pa.add (-omegap * std::pow (epsp, -1.0 / (double) N));
|
||||
|
||||
for (int i = 1; i <= L; ++i)
|
||||
{
|
||||
auto ui = (2 * i - 1.0) / (double) N;
|
||||
pa.add (omegap * std::pow (epsp, -1.0 / (double) N) * j * exp (ui * halfPi * j));
|
||||
}
|
||||
}
|
||||
else if (type == 1)
|
||||
{
|
||||
auto v0 = std::asinh (1.0 / epsp) / (N * halfPi);
|
||||
|
||||
if (r == 1)
|
||||
pa.add (-omegap * std::sinh (v0 * halfPi));
|
||||
|
||||
for (int i = 1; i <= L; ++i)
|
||||
{
|
||||
auto ui = (2 * i - 1.0) / (double) N;
|
||||
pa.add (omegap * j * std::cos ((ui - j * v0) * halfPi));
|
||||
}
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
auto v0 = std::asinh (epss) / (N * halfPi);
|
||||
|
||||
if (r == 1)
|
||||
pa.add(-1.0 / (k / omegap * std::sinh (v0 * halfPi)));
|
||||
|
||||
for (int i = 1; i <= L; ++i)
|
||||
{
|
||||
auto ui = (2 * i - 1.0) / (double) N;
|
||||
|
||||
pa.add (1.0 / (k / omegap * j * std::cos ((ui - j * v0) * halfPi)));
|
||||
za.add (1.0 / (k / omegap * j * std::cos (ui * halfPi)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v0 = -j * (SpecialFunctions::asne (j / epsp, k1) / (double) N);
|
||||
|
||||
if (r == 1)
|
||||
pa.add (omegap * j * SpecialFunctions::sne (j * v0, k));
|
||||
|
||||
for (int i = 1; i <= L; ++i)
|
||||
{
|
||||
auto ui = (2 * i - 1.0) / (double) N;
|
||||
auto zetai = SpecialFunctions::cde (ui, k);
|
||||
|
||||
pa.add (omegap * j * SpecialFunctions::cde (ui - j * v0, k));
|
||||
za.add (omegap * j / (k * zetai));
|
||||
}
|
||||
}
|
||||
|
||||
Array<Complex<double>> p, z, g;
|
||||
|
||||
if (r == 1)
|
||||
{
|
||||
p.add ((1.0 + pa[0]) / (1.0 - pa[0]));
|
||||
g.add (0.5 * (1.0 - p[0]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < L; ++i)
|
||||
{
|
||||
p.add ((1.0 + pa[i + r]) / (1.0 - pa[i + r]));
|
||||
z.add (za.size() == 0 ? -1.0 : (1.0 + za[i]) / (1.0 - za[i]));
|
||||
g.add ((1.0 - p[i + r]) / (1.0 - z[i]));
|
||||
}
|
||||
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>> cascadedCoefficients;
|
||||
|
||||
if (r == 1)
|
||||
{
|
||||
auto b0 = static_cast<FloatType> (H0 * std::real (g[0]));
|
||||
auto b1 = b0;
|
||||
auto a1 = static_cast<FloatType> (-std::real (p[0]));
|
||||
|
||||
cascadedCoefficients.add (new IIR::Coefficients<FloatType> (b0, b1, 1.0f, a1));
|
||||
}
|
||||
|
||||
for (int i = 0; i < L; ++i)
|
||||
{
|
||||
auto gain = std::pow (std::abs (g[i + r]), 2.0);
|
||||
|
||||
auto b0 = static_cast<FloatType> (gain);
|
||||
auto b1 = static_cast<FloatType> (std::real (-z[i] - std::conj (z[i])) * gain);
|
||||
auto b2 = static_cast<FloatType> (std::real ( z[i] * std::conj (z[i])) * gain);
|
||||
|
||||
auto a1 = static_cast<FloatType> (std::real (-p[i+r] - std::conj (p[i + r])));
|
||||
auto a2 = static_cast<FloatType> (std::real ( p[i+r] * std::conj (p[i + r])));
|
||||
|
||||
cascadedCoefficients.add (new IIR::Coefficients<FloatType> (b0, b1, b2, 1, a1, a2));
|
||||
}
|
||||
|
||||
return cascadedCoefficients;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRLowpassHighOrderButterworthMethod (FloatType frequency,
|
||||
double sampleRate, int order)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
jassert (order > 0);
|
||||
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>> arrayFilters;
|
||||
|
||||
if (order % 2 == 1)
|
||||
{
|
||||
arrayFilters.add (*IIR::Coefficients<FloatType>::makeFirstOrderLowPass (sampleRate, frequency));
|
||||
|
||||
for (int i = 0; i < order / 2; ++i)
|
||||
{
|
||||
auto Q = 1.0 / (2.0 * std::cos ((i + 1.0) * MathConstants<double>::pi / order));
|
||||
arrayFilters.add (*IIR::Coefficients<FloatType>::makeLowPass (sampleRate, frequency,
|
||||
static_cast<FloatType> (Q)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < order / 2; ++i)
|
||||
{
|
||||
auto Q = 1.0 / (2.0 * std::cos ((2.0 * i + 1.0) * MathConstants<double>::pi / (order * 2.0)));
|
||||
arrayFilters.add (*IIR::Coefficients<FloatType>::makeLowPass (sampleRate, frequency,
|
||||
static_cast<FloatType> (Q)));
|
||||
}
|
||||
}
|
||||
|
||||
return arrayFilters;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>>
|
||||
FilterDesign<FloatType>::designIIRHighpassHighOrderButterworthMethod (FloatType frequency,
|
||||
double sampleRate, int order)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
jassert (order > 0);
|
||||
|
||||
ReferenceCountedArray<IIR::Coefficients<FloatType>> arrayFilters;
|
||||
|
||||
if (order % 2 == 1)
|
||||
{
|
||||
arrayFilters.add (*IIR::Coefficients<FloatType>::makeFirstOrderHighPass (sampleRate, frequency));
|
||||
|
||||
for (int i = 0; i < order / 2; ++i)
|
||||
{
|
||||
auto Q = 1.0 / (2.0 * std::cos ((i + 1.0) * MathConstants<double>::pi / order));
|
||||
arrayFilters.add (*IIR::Coefficients<FloatType>::makeHighPass (sampleRate, frequency,
|
||||
static_cast<FloatType> (Q)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < order / 2; ++i)
|
||||
{
|
||||
auto Q = 1.0 / (2.0 * std::cos ((2.0 * i + 1.0) * MathConstants<double>::pi / (order * 2.0)));
|
||||
arrayFilters.add (*IIR::Coefficients<FloatType>::makeHighPass (sampleRate, frequency,
|
||||
static_cast<FloatType> (Q)));
|
||||
}
|
||||
}
|
||||
|
||||
return arrayFilters;
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
typename FilterDesign<FloatType>::IIRPolyphaseAllpassStructure
|
||||
FilterDesign<FloatType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (FloatType normalisedTransitionWidth,
|
||||
FloatType stopbandAmplitudedB)
|
||||
{
|
||||
jassert (normalisedTransitionWidth > 0 && normalisedTransitionWidth <= 0.5);
|
||||
jassert (stopbandAmplitudedB > -300 && stopbandAmplitudedB < -10);
|
||||
|
||||
const double wt = MathConstants<double>::twoPi * normalisedTransitionWidth;
|
||||
const double ds = Decibels::decibelsToGain (stopbandAmplitudedB, static_cast<FloatType> (-300.0));
|
||||
|
||||
auto k = std::pow (std::tan ((MathConstants<double>::pi - wt) / 4), 2.0);
|
||||
auto kp = std::sqrt (1.0 - k * k);
|
||||
auto e = (1 - std::sqrt (kp)) / (1 + std::sqrt (kp)) * 0.5;
|
||||
auto q = e + 2 * std::pow (e, 5.0) + 15 * std::pow (e, 9.0) + 150 * std::pow (e, 13.0);
|
||||
|
||||
auto k1 = ds * ds / (1 - ds * ds);
|
||||
int n = roundToInt (std::ceil (std::log (k1 * k1 / 16) / std::log (q)));
|
||||
|
||||
if (n % 2 == 0)
|
||||
++n;
|
||||
|
||||
if (n == 1)
|
||||
n = 3;
|
||||
|
||||
auto q1 = std::pow (q, (double) n);
|
||||
k1 = 4 * std::sqrt (q1);
|
||||
|
||||
const int N = (n - 1) / 2;
|
||||
Array<double> ai;
|
||||
|
||||
for (int i = 1; i <= N; ++i)
|
||||
{
|
||||
double num = 0.0;
|
||||
double delta = 1.0;
|
||||
int m = 0;
|
||||
|
||||
while (std::abs (delta) > 1e-100)
|
||||
{
|
||||
delta = std::pow (-1, m) * std::pow (q, m * (m + 1))
|
||||
* std::sin ((2 * m + 1) * MathConstants<double>::pi * i / (double) n);
|
||||
num += delta;
|
||||
m++;
|
||||
}
|
||||
|
||||
num *= 2 * std::pow (q, 0.25);
|
||||
|
||||
double den = 0.0;
|
||||
delta = 1.0;
|
||||
m = 1;
|
||||
|
||||
while (std::abs (delta) > 1e-100)
|
||||
{
|
||||
delta = std::pow (-1, m) * std::pow (q, m * m)
|
||||
* std::cos (m * MathConstants<double>::twoPi * i / (double) n);
|
||||
den += delta;
|
||||
++m;
|
||||
}
|
||||
|
||||
den = 1 + 2 * den;
|
||||
|
||||
auto wi = num / den;
|
||||
auto api = std::sqrt ((1 - wi * wi * k) * (1 - wi * wi / k)) / (1 + wi * wi);
|
||||
|
||||
ai.add ((1 - api) / (1 + api));
|
||||
}
|
||||
|
||||
IIRPolyphaseAllpassStructure structure;
|
||||
|
||||
for (int i = 0; i < N; i += 2)
|
||||
structure.directPath.add (new IIR::Coefficients<FloatType> (static_cast<FloatType> (ai[i]),
|
||||
0, 1, 1, 0, static_cast<FloatType> (ai[i])));
|
||||
|
||||
structure.delayedPath.add (new IIR::Coefficients<FloatType> (0, 1, 1, 0));
|
||||
|
||||
for (int i = 1; i < N; i += 2)
|
||||
structure.delayedPath.add (new IIR::Coefficients<FloatType> (static_cast<FloatType> (ai[i]),
|
||||
0, 1, 1, 0, static_cast<FloatType> (ai[i])));
|
||||
|
||||
structure.alpha.addArray (ai);
|
||||
|
||||
return structure;
|
||||
}
|
||||
|
||||
|
||||
template struct FilterDesign<float>;
|
||||
template struct FilterDesign<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
299
deps/juce/modules/juce_dsp/filter_design/juce_FilterDesign.h
vendored
Normal file
299
deps/juce/modules/juce_dsp/filter_design/juce_FilterDesign.h
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
This class provides a set of functions which generates FIR::Coefficients
|
||||
and IIR::Coefficients, of high-order low-pass filters. They can be used
|
||||
for processing directly audio as an equalizer, in resampling algorithms etc.
|
||||
|
||||
see FIRFilter::Coefficients, FIRFilter, WindowingFunction, IIRFilter::Coefficients, IIRFilter
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
struct FilterDesign
|
||||
{
|
||||
using FIRCoefficientsPtr = typename FIR::Coefficients<FloatType>::Ptr;
|
||||
using IIRCoefficients = typename IIR::Coefficients<FloatType>;
|
||||
|
||||
using WindowingMethod = typename WindowingFunction<FloatType>::WindowingMethod;
|
||||
|
||||
//==============================================================================
|
||||
/** This method generates a FIR::Coefficients for a low-pass filter, using
|
||||
the windowing design method, applied to a sinc impulse response. It is one
|
||||
of the simplest method used to generate a high order low-pass filter, which
|
||||
has the downside of needing more coefficients than more complex method to
|
||||
perform a given attenuation in the stop band.
|
||||
|
||||
It generates linear phase filters coefficients.
|
||||
|
||||
Note: The flatTop WindowingMethod generates an impulse response with a
|
||||
maximum amplitude higher than one, and might be normalised if necessary
|
||||
depending on the applications.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param order the order of the filter
|
||||
@param type the type, must be a WindowingFunction::WindowingType
|
||||
@param beta an optional additional parameter useful for the Kaiser windowing function
|
||||
*/
|
||||
|
||||
static FIRCoefficientsPtr designFIRLowpassWindowMethod (FloatType frequency, double sampleRate,
|
||||
size_t order, WindowingMethod type,
|
||||
FloatType beta = static_cast<FloatType> (2));
|
||||
|
||||
/** This a variant of the function designFIRLowpassWindowMethod, which allows the
|
||||
user to specify a transition width and a negative gain in dB,
|
||||
to get a low-pass filter using the Kaiser windowing function, with calculated
|
||||
values of the filter order and of the beta parameter, to satisfy the constraints.
|
||||
|
||||
It generates linear phase filters coefficients.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param amplitudedB the maximum amplitude in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
|
||||
static FIRCoefficientsPtr designFIRLowpassKaiserMethod (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType amplitudedB);
|
||||
|
||||
|
||||
/** This method is also a variant of the function designFIRLowpassWindowMethod, using
|
||||
a rectangular window as a basis, and a spline transition between the pass band and
|
||||
the stop band, to reduce the Gibbs phenomenon.
|
||||
|
||||
It generates linear phase filters coefficients.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param order the order of the filter
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param spline between 1.0 and 4.0, indicates how much the transition
|
||||
is curved, with 1.0 meaning a straight line
|
||||
*/
|
||||
static FIRCoefficientsPtr designFIRLowpassTransitionMethod (FloatType frequency, double sampleRate,
|
||||
size_t order,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType spline);
|
||||
|
||||
/** This method generates a FIR::Coefficients for a low-pass filter, by
|
||||
minimizing the average error between the generated filter and an ideal one
|
||||
using the least squares error criterion and matrices operations.
|
||||
|
||||
It generates linear phase filters coefficients.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param order the order of the filter
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param stopBandWeight between 1.0 and 100.0, indicates how much we want
|
||||
attenuation in the stop band, against some oscillation
|
||||
in the pass band
|
||||
*/
|
||||
static FIRCoefficientsPtr designFIRLowpassLeastSquaresMethod (FloatType frequency, double sampleRate, size_t order,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType stopBandWeight);
|
||||
|
||||
/** This method generates a FIR::Coefficients for a low-pass filter, with
|
||||
a cutoff frequency at half band, using an algorithm described in the article
|
||||
"Design of Half-Band FIR Filters for Signal Compression" from Pavel
|
||||
Zahradnik, to get an equiripple like high order FIR filter, without the need
|
||||
of an iterative method and convergence failure risks.
|
||||
|
||||
It generates linear phase filters coefficients.
|
||||
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param amplitudedB the maximum amplitude in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
static FIRCoefficientsPtr designFIRLowpassHalfBandEquirippleMethod (FloatType normalisedTransitionWidth,
|
||||
FloatType amplitudedB);
|
||||
|
||||
//==============================================================================
|
||||
/** This method returns an array of IIR::Coefficients, made to be used in
|
||||
cascaded IIRFilters, providing a minimum phase low-pass filter without any
|
||||
ripple in the pass band and in the stop band.
|
||||
|
||||
The algorithms are based on "Lecture Notes on Elliptic Filter Design" by
|
||||
Sophocles J. Orfanidis.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param passbandAmplitudedB the highest gain in dB expected in the pass band (must be negative)
|
||||
@param stopbandAmplitudedB the gain in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRLowpassHighOrderButterworthMethod (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB);
|
||||
|
||||
//==============================================================================
|
||||
/** This method returns an array of IIR::Coefficients, made to be used in
|
||||
cascaded IIRFilters, providing a minimum phase low-pass filter without any
|
||||
ripple in the pass band and in the stop band.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param order the order of the resulting IIR filter, providing
|
||||
an attenuation of -6 dB times order / octave
|
||||
*/
|
||||
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRLowpassHighOrderButterworthMethod (FloatType frequency, double sampleRate,
|
||||
int order);
|
||||
|
||||
/** This method returns an array of IIR::Coefficients, made to be used in
|
||||
cascaded IIRFilters, providing a minimum phase high-pass filter without any
|
||||
ripple in the pass band and in the stop band.
|
||||
|
||||
@param frequency the cutoff frequency of the high-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param order the order of the resulting IIR filter, providing
|
||||
an attenuation of -6 dB times order / octave
|
||||
*/
|
||||
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRHighpassHighOrderButterworthMethod (FloatType frequency, double sampleRate,
|
||||
int order);
|
||||
|
||||
/** This method returns an array of IIR::Coefficients, made to be used in
|
||||
cascaded IIRFilters, providing a minimum phase low-pass filter without any
|
||||
ripple in the stop band only.
|
||||
|
||||
The algorithms are based on "Lecture Notes on Elliptic Filter Design" by
|
||||
Sophocles J. Orfanidis.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param passbandAmplitudedB the highest amplitude in dB expected in the pass band (must be negative)
|
||||
@param stopbandAmplitudedB the lowest amplitude in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRLowpassHighOrderChebyshev1Method (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB);
|
||||
|
||||
/** This method returns an array of IIR::Coefficients, made to be used in
|
||||
cascaded IIRFilters, providing a minimum phase low-pass filter without any
|
||||
ripple in the pass band only.
|
||||
|
||||
The algorithms are based on "Lecture Notes on Elliptic Filter Design" by
|
||||
Sophocles J. Orfanidis.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param passbandAmplitudedB the highest amplitude in dB expected in the pass band (must be negative)
|
||||
@param stopbandAmplitudedB the lowest amplitude in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRLowpassHighOrderChebyshev2Method (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB);
|
||||
|
||||
/** This method returns an array of IIR::Coefficients, made to be used in
|
||||
cascaded IIR::Filters, providing a minimum phase low-pass filter with ripples
|
||||
in both the pass band and in the stop band.
|
||||
|
||||
The algorithms are based on "Lecture Notes on Elliptic Filter Design" by
|
||||
Sophocles J. Orfanidis.
|
||||
|
||||
@param frequency the cutoff frequency of the low-pass filter
|
||||
@param sampleRate the sample rate being used in the filter design
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param passbandAmplitudedB the highest amplitude in dB expected in the pass band (must be negative)
|
||||
@param stopbandAmplitudedB the lowest amplitude in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRLowpassHighOrderEllipticMethod (FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB);
|
||||
|
||||
/** The structure returned by the function designIIRLowpassHalfBandPolyphaseAllpassMethod.
|
||||
|
||||
The two first members of this structure directPath and delayedPath are arrays of
|
||||
IIR::Coefficients, made of polyphase second order allpass filters and an additional
|
||||
delay in the second array, that can be used in cascaded filters processed in two
|
||||
parallel paths, which must be summed at the end to get the high order efficient
|
||||
low-pass filtering. The last member is an array with the useful parameters for
|
||||
simulating the structure using any custom processing function.
|
||||
*/
|
||||
struct IIRPolyphaseAllpassStructure
|
||||
{
|
||||
ReferenceCountedArray<IIRCoefficients> directPath, delayedPath;
|
||||
Array<double> alpha;
|
||||
};
|
||||
|
||||
/** This method generates arrays of IIR::Coefficients for a low-pass filter, with
|
||||
a cutoff frequency at half band, using an algorithm described in the article
|
||||
"Digital Signal Processing Schemes for efficient interpolation and decimation" from
|
||||
Pavel Valenzuela and Constantinides.
|
||||
|
||||
The result is a IIRPolyphaseAllpassStructure object.
|
||||
|
||||
The two members of this structure directPath and delayedPath are arrays of
|
||||
IIR::Coefficients, made of polyphase second order allpass filters and an additional
|
||||
delay in the second array, that can be used in cascaded filters processed in two
|
||||
parallel paths, which must be summed at the end to get the high order efficient
|
||||
low-pass filtering.
|
||||
|
||||
The gain of the resulting pass-band is 6 dB, so don't forget to compensate it if you
|
||||
want to use that method for something else than two times oversampling.
|
||||
|
||||
@param normalisedTransitionWidth the normalised size between 0 and 0.5 of the transition
|
||||
between the pass band and the stop band
|
||||
@param stopbandAmplitudedB the maximum amplitude in dB expected in the stop band (must be negative)
|
||||
*/
|
||||
static IIRPolyphaseAllpassStructure designIIRLowpassHalfBandPolyphaseAllpassMethod (FloatType normalisedTransitionWidth,
|
||||
FloatType stopbandAmplitudedB);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static Array<double> getPartialImpulseResponseHn (int n, double kp);
|
||||
|
||||
static ReferenceCountedArray<IIRCoefficients> designIIRLowpassHighOrderGeneralMethod (int type, FloatType frequency, double sampleRate,
|
||||
FloatType normalisedTransitionWidth,
|
||||
FloatType passbandAmplitudedB,
|
||||
FloatType stopbandAmplitudedB);
|
||||
FilterDesign() = delete;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
1297
deps/juce/modules/juce_dsp/frequency/juce_Convolution.cpp
vendored
Normal file
1297
deps/juce/modules/juce_dsp/frequency/juce_Convolution.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
310
deps/juce/modules/juce_dsp/frequency/juce_Convolution.h
vendored
Normal file
310
deps/juce/modules/juce_dsp/frequency/juce_Convolution.h
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Used by the Convolution to dispatch engine-update messages on a background
|
||||
thread.
|
||||
|
||||
May be shared between multiple Convolution instances.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
class JUCE_API ConvolutionMessageQueue
|
||||
{
|
||||
public:
|
||||
/** Initialises the queue to a default size.
|
||||
|
||||
If your Convolution is updated very frequently, or you are sharing
|
||||
this queue between multiple Convolutions, consider using the alternative
|
||||
constructor taking an explicit size argument.
|
||||
*/
|
||||
ConvolutionMessageQueue();
|
||||
~ConvolutionMessageQueue() noexcept;
|
||||
|
||||
/** Initialises the queue with the specified number of entries.
|
||||
|
||||
In general, the number of required entries scales with the number
|
||||
of Convolutions sharing the same Queue, and the frequency of updates
|
||||
to those Convolutions.
|
||||
*/
|
||||
explicit ConvolutionMessageQueue (int numEntries);
|
||||
|
||||
ConvolutionMessageQueue (ConvolutionMessageQueue&&) noexcept;
|
||||
ConvolutionMessageQueue& operator= (ConvolutionMessageQueue&&) noexcept;
|
||||
|
||||
ConvolutionMessageQueue (const ConvolutionMessageQueue&) = delete;
|
||||
ConvolutionMessageQueue& operator= (const ConvolutionMessageQueue&) = delete;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
|
||||
friend class Convolution;
|
||||
};
|
||||
|
||||
/**
|
||||
Performs stereo partitioned convolution of an input signal with an
|
||||
impulse response in the frequency domain, using the JUCE FFT class.
|
||||
|
||||
This class provides some thread-safe functions to load impulse responses
|
||||
from audio files or memory on-the-fly without noticeable artefacts,
|
||||
performing resampling and trimming if necessary.
|
||||
|
||||
The processing performed by this class is equivalent to the time domain
|
||||
convolution done in the FIRFilter class, with a FIRFilter::Coefficients
|
||||
object having the samples of the impulse response as its coefficients.
|
||||
However, in general it is more efficient to do frequency domain
|
||||
convolution when the size of the impulse response is 64 samples or
|
||||
greater.
|
||||
|
||||
Note: The default operation of this class uses zero latency and a uniform
|
||||
partitioned algorithm. If the impulse response size is large, or if the
|
||||
algorithm is too CPU intensive, it is possible to use either a fixed
|
||||
latency version of the algorithm, or a simple non-uniform partitioned
|
||||
convolution algorithm.
|
||||
|
||||
Threading: It is not safe to interleave calls to the methods of this
|
||||
class. If you need to load new impulse responses during processing the
|
||||
load() calls must be synchronised with process() calls, which in practice
|
||||
means making the load() call from the audio thread. The
|
||||
loadImpulseResponse() functions *are* wait-free and are therefore
|
||||
suitable for use in a realtime context.
|
||||
|
||||
@see FIRFilter, FIRFilter::Coefficients, FFT
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
class JUCE_API Convolution
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Initialises an object for performing convolution in the frequency domain. */
|
||||
Convolution();
|
||||
|
||||
/** Initialises a convolution engine using a shared background message queue.
|
||||
|
||||
IMPORTANT: the queue *must* remain alive throughout the lifetime of the
|
||||
Convolution.
|
||||
*/
|
||||
explicit Convolution (ConvolutionMessageQueue& queue);
|
||||
|
||||
/** Contains configuration information for a convolution with a fixed latency. */
|
||||
struct Latency { int latencyInSamples; };
|
||||
|
||||
/** Initialises an object for performing convolution with a fixed latency.
|
||||
|
||||
If the requested latency is zero, the actual latency will also be zero.
|
||||
For requested latencies greater than zero, the actual latency will
|
||||
always at least as large as the requested latency. Using a fixed
|
||||
non-zero latency can reduce the CPU consumption of the convolution
|
||||
algorithm.
|
||||
|
||||
@param requiredLatency the minimum latency
|
||||
*/
|
||||
explicit Convolution (const Latency& requiredLatency);
|
||||
|
||||
/** Contains configuration information for a non-uniform convolution. */
|
||||
struct NonUniform { int headSizeInSamples; };
|
||||
|
||||
/** Initialises an object for performing convolution in the frequency domain
|
||||
using a non-uniform partitioned algorithm.
|
||||
|
||||
A requiredHeadSize of 256 samples or greater will improve the
|
||||
efficiency of the processing for IR sizes of 4096 samples or greater
|
||||
(recommended for reverberation IRs).
|
||||
|
||||
@param requiredHeadSize the head IR size for two stage non-uniform
|
||||
partitioned convolution
|
||||
*/
|
||||
explicit Convolution (const NonUniform& requiredHeadSize);
|
||||
|
||||
/** Behaves the same as the constructor taking a single Latency argument,
|
||||
but with a shared background message queue.
|
||||
|
||||
IMPORTANT: the queue *must* remain alive throughout the lifetime of the
|
||||
Convolution.
|
||||
*/
|
||||
Convolution (const Latency&, ConvolutionMessageQueue&);
|
||||
|
||||
/** Behaves the same as the constructor taking a single NonUniform argument,
|
||||
but with a shared background message queue.
|
||||
|
||||
IMPORTANT: the queue *must* remain alive throughout the lifetime of the
|
||||
Convolution.
|
||||
*/
|
||||
Convolution (const NonUniform&, ConvolutionMessageQueue&);
|
||||
|
||||
~Convolution() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Must be called before first calling process.
|
||||
|
||||
In general, calls to loadImpulseResponse() load the impulse response (IR)
|
||||
asynchronously. The IR will become active once it has been completely loaded
|
||||
and processed, which may take some time.
|
||||
|
||||
Calling prepare() will ensure that the IR supplied to the most recent call to
|
||||
loadImpulseResponse() is fully initialised. This IR will then be active during
|
||||
the next call to process(). It is recommended to call loadImpulseResponse() *before*
|
||||
prepare() if a specific IR must be active during the first process() call.
|
||||
*/
|
||||
void prepare (const ProcessSpec&);
|
||||
|
||||
/** Resets the processing pipeline ready to start a new stream of data. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Performs the filter operation on the given set of samples with optional
|
||||
stereo processing.
|
||||
*/
|
||||
template <typename ProcessContext,
|
||||
std::enable_if_t<std::is_same<typename ProcessContext::SampleType, float>::value, int> = 0>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
processSamples (context.getInputBlock(), context.getOutputBlock(), context.isBypassed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
enum class Stereo { no, yes };
|
||||
enum class Trim { no, yes };
|
||||
enum class Normalise { no, yes };
|
||||
|
||||
//==============================================================================
|
||||
/** This function loads an impulse response audio file from memory, added in a
|
||||
JUCE project with the Projucer as binary data. It can load any of the audio
|
||||
formats registered in JUCE, and performs some resampling and pre-processing
|
||||
as well if needed.
|
||||
|
||||
Note: Don't try to use this function on float samples, since the data is
|
||||
expected to be an audio file in its binary format. Be sure that the original
|
||||
data remains constant throughout the lifetime of the Convolution object, as
|
||||
the loading process will happen on a background thread once this function has
|
||||
returned.
|
||||
|
||||
@param sourceData the block of data to use as the stream's source
|
||||
@param sourceDataSize the number of bytes in the source data block
|
||||
@param isStereo selects either stereo or mono
|
||||
@param requiresTrimming optionally trim the start and the end of the impulse response
|
||||
@param size the expected size for the impulse response after loading, can be
|
||||
set to 0 to requesting the original impulse response size
|
||||
@param requiresNormalisation optionally normalise the impulse response amplitude
|
||||
*/
|
||||
void loadImpulseResponse (const void* sourceData, size_t sourceDataSize,
|
||||
Stereo isStereo, Trim requiresTrimming, size_t size,
|
||||
Normalise requiresNormalisation = Normalise::yes);
|
||||
|
||||
/** This function loads an impulse response from an audio file. It can load any
|
||||
of the audio formats registered in JUCE, and performs some resampling and
|
||||
pre-processing as well if needed.
|
||||
|
||||
@param fileImpulseResponse the location of the audio file
|
||||
@param isStereo selects either stereo or mono
|
||||
@param requiresTrimming optionally trim the start and the end of the impulse response
|
||||
@param size the expected size for the impulse response after loading, can be
|
||||
set to 0 to requesting the original impulse response size
|
||||
@param requiresNormalisation optionally normalise the impulse response amplitude
|
||||
*/
|
||||
void loadImpulseResponse (const File& fileImpulseResponse,
|
||||
Stereo isStereo, Trim requiresTrimming, size_t size,
|
||||
Normalise requiresNormalisation = Normalise::yes);
|
||||
|
||||
/** This function loads an impulse response from an audio buffer.
|
||||
To avoid memory allocation on the audio thread, this function takes
|
||||
ownership of the buffer passed in.
|
||||
|
||||
If calling this function during processing, make sure that the buffer is
|
||||
not allocated on the audio thread (be careful of accidental copies!).
|
||||
If you need to pass arbitrary/generated buffers it's recommended to
|
||||
create these buffers on a separate thread and to use some wait-free
|
||||
construct (a lock-free queue or a SpinLock/GenericScopedTryLock combination)
|
||||
to transfer ownership to the audio thread without allocating.
|
||||
|
||||
@param buffer the AudioBuffer to use
|
||||
@param bufferSampleRate the sampleRate of the data in the AudioBuffer
|
||||
@param isStereo selects either stereo or mono
|
||||
@param requiresTrimming optionally trim the start and the end of the impulse response
|
||||
@param requiresNormalisation optionally normalise the impulse response amplitude
|
||||
*/
|
||||
void loadImpulseResponse (AudioBuffer<float>&& buffer, double bufferSampleRate,
|
||||
Stereo isStereo, Trim requiresTrimming, Normalise requiresNormalisation);
|
||||
|
||||
/** This function returns the size of the current IR in samples. */
|
||||
int getCurrentIRSize() const;
|
||||
|
||||
/** This function returns the current latency of the process in samples.
|
||||
|
||||
Note: This is the latency of the convolution engine, not the latency
|
||||
associated with the current impulse response choice that has to be
|
||||
considered separately (linear phase filters, for example).
|
||||
*/
|
||||
int getLatency() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Convolution (const Latency&,
|
||||
const NonUniform&,
|
||||
OptionalScopedPointer<ConvolutionMessageQueue>&&);
|
||||
|
||||
void processSamples (const AudioBlock<const float>&, AudioBlock<float>&, bool isBypassed) noexcept;
|
||||
|
||||
class Mixer
|
||||
{
|
||||
public:
|
||||
void prepare (const ProcessSpec&);
|
||||
|
||||
template <typename ProcessWet>
|
||||
void processSamples (const AudioBlock<const float>&,
|
||||
AudioBlock<float>&,
|
||||
bool isBypassed,
|
||||
ProcessWet&&) noexcept;
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::array<SmoothedValue<float>, 2> volumeDry, volumeWet;
|
||||
AudioBlock<float> dryBlock;
|
||||
HeapBlock<char> dryBlockStorage;
|
||||
double sampleRate = 0;
|
||||
bool currentIsBypassed = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
|
||||
//==============================================================================
|
||||
Mixer mixer;
|
||||
bool isActive = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Convolution)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
581
deps/juce/modules/juce_dsp/frequency/juce_Convolution_test.cpp
vendored
Normal file
581
deps/juce/modules/juce_dsp/frequency/juce_Convolution_test.cpp
vendored
Normal file
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 ConvolutionTest : public UnitTest
|
||||
{
|
||||
template <typename Callback>
|
||||
static void nTimes (int n, Callback&& callback)
|
||||
{
|
||||
for (auto i = 0; i < n; ++i)
|
||||
callback();
|
||||
}
|
||||
|
||||
static AudioBuffer<float> makeRamp (int length)
|
||||
{
|
||||
AudioBuffer<float> result (1, length);
|
||||
result.clear();
|
||||
|
||||
const auto writePtr = result.getWritePointer (0);
|
||||
std::fill (writePtr, writePtr + length, 1.0f);
|
||||
result.applyGainRamp (0, length, 1.0f, 0.0f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static AudioBuffer<float> makeStereoRamp (int length)
|
||||
{
|
||||
AudioBuffer<float> result (2, length);
|
||||
result.clear();
|
||||
|
||||
auto** channels = result.getArrayOfWritePointers();
|
||||
std::for_each (channels, channels + result.getNumChannels(), [length] (auto* channel)
|
||||
{
|
||||
std::fill (channel, channel + length, 1.0f);
|
||||
});
|
||||
|
||||
result.applyGainRamp (0, 0, length, 1.0f, 0.0f);
|
||||
result.applyGainRamp (1, 0, length, 0.0f, 1.0f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void addDiracImpulse (const AudioBlock<float>& block)
|
||||
{
|
||||
block.clear();
|
||||
|
||||
for (size_t channel = 0; channel != block.getNumChannels(); ++channel)
|
||||
block.setSample ((int) channel, 0, 1.0f);
|
||||
}
|
||||
|
||||
void checkForNans (const AudioBlock<float>& block)
|
||||
{
|
||||
for (size_t channel = 0; channel != block.getNumChannels(); ++channel)
|
||||
for (size_t sample = 0; sample != block.getNumSamples(); ++sample)
|
||||
expect (! std::isnan (block.getSample ((int) channel, (int) sample)));
|
||||
}
|
||||
|
||||
void checkAllChannelsNonZero (const AudioBlock<float>& block)
|
||||
{
|
||||
for (size_t i = 0; i != block.getNumChannels(); ++i)
|
||||
{
|
||||
const auto* channel = block.getChannelPointer (i);
|
||||
|
||||
expect (std::any_of (channel, channel + block.getNumSamples(), [] (float sample)
|
||||
{
|
||||
return sample != 0.0f;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void nonAllocatingExpectWithinAbsoluteError (const T& a, const T& b, const T& error)
|
||||
{
|
||||
expect (std::abs (a - b) < error);
|
||||
}
|
||||
|
||||
enum class InitSequence { prepareThenLoad, loadThenPrepare };
|
||||
|
||||
void checkLatency (const Convolution& convolution, const Convolution::Latency& latency)
|
||||
{
|
||||
const auto reportedLatency = convolution.getLatency();
|
||||
|
||||
if (latency.latencyInSamples == 0)
|
||||
expect (reportedLatency == 0);
|
||||
|
||||
expect (reportedLatency >= latency.latencyInSamples);
|
||||
}
|
||||
|
||||
void checkLatency (const Convolution&, const Convolution::NonUniform&) {}
|
||||
|
||||
template <typename ConvolutionConfig>
|
||||
void testConvolution (const ProcessSpec& spec,
|
||||
const ConvolutionConfig& config,
|
||||
const AudioBuffer<float>& ir,
|
||||
double irSampleRate,
|
||||
Convolution::Stereo stereo,
|
||||
Convolution::Trim trim,
|
||||
Convolution::Normalise normalise,
|
||||
const AudioBlock<const float>& expectedResult,
|
||||
InitSequence initSequence)
|
||||
{
|
||||
AudioBuffer<float> buffer (static_cast<int> (spec.numChannels),
|
||||
static_cast<int> (spec.maximumBlockSize));
|
||||
AudioBlock<float> block { buffer };
|
||||
ProcessContextReplacing<float> context { block };
|
||||
|
||||
const auto numBlocksPerSecond = (int) std::ceil (spec.sampleRate / spec.maximumBlockSize);
|
||||
const auto numBlocksForImpulse = (int) std::ceil ((double) expectedResult.getNumSamples() / spec.maximumBlockSize);
|
||||
|
||||
AudioBuffer<float> outBuffer (static_cast<int> (spec.numChannels),
|
||||
numBlocksForImpulse * static_cast<int> (spec.maximumBlockSize));
|
||||
|
||||
Convolution convolution (config);
|
||||
|
||||
auto copiedIr = ir;
|
||||
|
||||
if (initSequence == InitSequence::loadThenPrepare)
|
||||
convolution.loadImpulseResponse (std::move (copiedIr), irSampleRate, stereo, trim, normalise);
|
||||
|
||||
convolution.prepare (spec);
|
||||
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
if (initSequence == InitSequence::prepareThenLoad)
|
||||
convolution.loadImpulseResponse (std::move (copiedIr), irSampleRate, stereo, trim, normalise);
|
||||
|
||||
checkLatency (convolution, config);
|
||||
|
||||
auto processBlocksWithDiracImpulse = [&]
|
||||
{
|
||||
for (auto i = 0; i != numBlocksForImpulse; ++i)
|
||||
{
|
||||
if (i == 0)
|
||||
addDiracImpulse (block);
|
||||
else
|
||||
block.clear();
|
||||
|
||||
convolution.process (context);
|
||||
|
||||
for (auto c = 0; c != static_cast<int> (spec.numChannels); ++c)
|
||||
{
|
||||
outBuffer.copyFrom (c,
|
||||
i * static_cast<int> (spec.maximumBlockSize),
|
||||
block.getChannelPointer (static_cast<size_t> (c)),
|
||||
static_cast<int> (spec.maximumBlockSize));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If we load an IR while the convolution is already running, we'll need to wait
|
||||
// for it to be loaded on a background thread
|
||||
if (initSequence == InitSequence::prepareThenLoad)
|
||||
{
|
||||
const auto time = Time::getMillisecondCounter();
|
||||
|
||||
// Wait 10 seconds to load the impulse response
|
||||
while (Time::getMillisecondCounter() - time < 10'000)
|
||||
{
|
||||
processBlocksWithDiracImpulse();
|
||||
|
||||
// Check if the impulse response was loaded
|
||||
if (block.getSample (0, 1) != 0.0f)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, our convolution should be loaded and the current IR size should
|
||||
// match the expected result size
|
||||
expect (convolution.getCurrentIRSize() == static_cast<int> (expectedResult.getNumSamples()));
|
||||
|
||||
// Make sure we get any smoothing out of the way
|
||||
nTimes (numBlocksPerSecond, processBlocksWithDiracImpulse);
|
||||
|
||||
nTimes (5, [&]
|
||||
{
|
||||
processBlocksWithDiracImpulse();
|
||||
|
||||
const auto actualLatency = static_cast<size_t> (convolution.getLatency());
|
||||
|
||||
// The output should be the same as the IR
|
||||
for (size_t c = 0; c != static_cast<size_t> (expectedResult.getNumChannels()); ++c)
|
||||
{
|
||||
for (size_t i = 0; i != static_cast<size_t> (expectedResult.getNumSamples()); ++i)
|
||||
{
|
||||
const auto equivalentSample = i + actualLatency;
|
||||
|
||||
if (static_cast<int> (equivalentSample) >= outBuffer.getNumSamples())
|
||||
continue;
|
||||
|
||||
nonAllocatingExpectWithinAbsoluteError (outBuffer.getSample ((int) c, (int) equivalentSample),
|
||||
expectedResult.getSample ((int) c, (int) i),
|
||||
0.01f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename ConvolutionConfig>
|
||||
void testConvolution (const ProcessSpec& spec,
|
||||
const ConvolutionConfig& config,
|
||||
const AudioBuffer<float>& ir,
|
||||
double irSampleRate,
|
||||
Convolution::Stereo stereo,
|
||||
Convolution::Trim trim,
|
||||
Convolution::Normalise normalise,
|
||||
const AudioBlock<const float>& expectedResult)
|
||||
{
|
||||
for (const auto sequence : { InitSequence::prepareThenLoad, InitSequence::loadThenPrepare })
|
||||
testConvolution (spec, config, ir, irSampleRate, stereo, trim, normalise, expectedResult, sequence);
|
||||
}
|
||||
|
||||
public:
|
||||
ConvolutionTest()
|
||||
: UnitTest ("Convolution", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
const ProcessSpec spec { 44100.0, 512, 2 };
|
||||
AudioBuffer<float> buffer (static_cast<int> (spec.numChannels),
|
||||
static_cast<int> (spec.maximumBlockSize));
|
||||
AudioBlock<float> block { buffer };
|
||||
ProcessContextReplacing<float> context { block };
|
||||
|
||||
const auto impulseData = []
|
||||
{
|
||||
Random random;
|
||||
AudioBuffer<float> result (2, 1000);
|
||||
|
||||
for (auto channel = 0; channel != result.getNumChannels(); ++channel)
|
||||
for (auto sample = 0; sample != result.getNumSamples(); ++sample)
|
||||
result.setSample (channel, sample, random.nextFloat());
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
beginTest ("Impulse responses can be loaded without allocating on the audio thread");
|
||||
{
|
||||
Convolution convolution;
|
||||
convolution.prepare (spec);
|
||||
|
||||
auto copy = impulseData;
|
||||
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
nTimes (100, [&]
|
||||
{
|
||||
convolution.loadImpulseResponse (std::move (copy),
|
||||
1000,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no);
|
||||
addDiracImpulse (block);
|
||||
convolution.process (context);
|
||||
checkForNans (block);
|
||||
});
|
||||
}
|
||||
|
||||
beginTest ("Convolution can be reset without allocating on the audio thread");
|
||||
{
|
||||
Convolution convolution;
|
||||
convolution.prepare (spec);
|
||||
|
||||
auto copy = impulseData;
|
||||
|
||||
convolution.loadImpulseResponse (std::move (copy),
|
||||
1000,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::yes);
|
||||
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
nTimes (100, [&]
|
||||
{
|
||||
addDiracImpulse (block);
|
||||
convolution.reset();
|
||||
convolution.process (context);
|
||||
convolution.reset();
|
||||
});
|
||||
|
||||
checkForNans (block);
|
||||
}
|
||||
|
||||
beginTest ("Completely empty IRs don't crash");
|
||||
{
|
||||
AudioBuffer<float> emptyBuffer;
|
||||
|
||||
Convolution convolution;
|
||||
convolution.prepare (spec);
|
||||
|
||||
auto copy = impulseData;
|
||||
|
||||
convolution.loadImpulseResponse (std::move (copy),
|
||||
2000,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::yes);
|
||||
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
nTimes (100, [&]
|
||||
{
|
||||
addDiracImpulse (block);
|
||||
convolution.reset();
|
||||
convolution.process (context);
|
||||
convolution.reset();
|
||||
});
|
||||
|
||||
checkForNans (block);
|
||||
}
|
||||
|
||||
beginTest ("Convolutions can cope with a change in samplerate and blocksize");
|
||||
{
|
||||
Convolution convolution;
|
||||
|
||||
auto copy = impulseData;
|
||||
convolution.loadImpulseResponse (std::move (copy),
|
||||
2000,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::no,
|
||||
Convolution::Normalise::yes);
|
||||
|
||||
const dsp::ProcessSpec specs[] = { { 96'000.0, 1024, 2 },
|
||||
{ 48'000.0, 512, 2 },
|
||||
{ 44'100.0, 256, 2 } };
|
||||
|
||||
for (const auto& thisSpec : specs)
|
||||
{
|
||||
convolution.prepare (thisSpec);
|
||||
|
||||
expectWithinAbsoluteError ((double) convolution.getCurrentIRSize(),
|
||||
thisSpec.sampleRate * 0.5,
|
||||
1.0);
|
||||
|
||||
juce::AudioBuffer<float> thisBuffer ((int) thisSpec.numChannels,
|
||||
(int) thisSpec.maximumBlockSize);
|
||||
AudioBlock<float> thisBlock { thisBuffer };
|
||||
ProcessContextReplacing<float> thisContext { thisBlock };
|
||||
|
||||
nTimes (100, [&]
|
||||
{
|
||||
addDiracImpulse (thisBlock);
|
||||
convolution.process (thisContext);
|
||||
|
||||
checkForNans (thisBlock);
|
||||
checkAllChannelsNonZero (thisBlock);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Short uniform convolutions work");
|
||||
{
|
||||
const auto ramp = makeRamp (static_cast<int> (spec.maximumBlockSize) / 2);
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
ramp);
|
||||
}
|
||||
|
||||
beginTest ("Longer uniform convolutions work");
|
||||
{
|
||||
const auto ramp = makeRamp (static_cast<int> (spec.maximumBlockSize) * 8);
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
ramp);
|
||||
}
|
||||
|
||||
beginTest ("Normalisation works");
|
||||
{
|
||||
const auto ramp = makeRamp (static_cast<int> (spec.maximumBlockSize) * 8);
|
||||
|
||||
auto copy = ramp;
|
||||
const auto channels = copy.getArrayOfWritePointers();
|
||||
const auto numChannels = copy.getNumChannels();
|
||||
const auto numSamples = copy.getNumSamples();
|
||||
|
||||
const auto factor = 0.125f / std::sqrt (std::accumulate (channels, channels + numChannels, 0.0f,
|
||||
[numSamples] (auto max, auto* channel)
|
||||
{
|
||||
return juce::jmax (max, std::accumulate (channel, channel + numSamples, 0.0f,
|
||||
[] (auto sum, auto sample)
|
||||
{
|
||||
return sum + sample * sample;
|
||||
}));
|
||||
}));
|
||||
|
||||
std::for_each (channels, channels + numChannels, [factor, numSamples] (auto* channel)
|
||||
{
|
||||
FloatVectorOperations::multiply (channel, factor, numSamples);
|
||||
});
|
||||
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::yes,
|
||||
copy);
|
||||
}
|
||||
|
||||
beginTest ("Stereo convolutions work");
|
||||
{
|
||||
const auto ramp = makeStereoRamp (static_cast<int> (spec.maximumBlockSize) * 5);
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
ramp);
|
||||
}
|
||||
|
||||
beginTest ("Stereo IRs only use first channel if stereo is disabled");
|
||||
{
|
||||
const auto length = static_cast<int> (spec.maximumBlockSize) * 5;
|
||||
const auto ramp = makeStereoRamp (length);
|
||||
|
||||
const float* channels[] { ramp.getReadPointer (0), ramp.getReadPointer (0) };
|
||||
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::no,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
AudioBlock<const float> (channels, numElementsInArray (channels), length));
|
||||
}
|
||||
|
||||
beginTest ("IRs with extra silence are trimmed appropriately");
|
||||
{
|
||||
const auto length = static_cast<int> (spec.maximumBlockSize) * 3;
|
||||
const auto ramp = makeRamp (length);
|
||||
AudioBuffer<float> paddedRamp (ramp.getNumChannels(), ramp.getNumSamples() * 2);
|
||||
paddedRamp.clear();
|
||||
|
||||
const auto offset = (paddedRamp.getNumSamples() - ramp.getNumSamples()) / 2;
|
||||
|
||||
for (auto channel = 0; channel != ramp.getNumChannels(); ++channel)
|
||||
paddedRamp.copyFrom (channel, offset, ramp.getReadPointer (channel), length);
|
||||
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
paddedRamp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::no,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
ramp);
|
||||
}
|
||||
|
||||
beginTest ("IRs are resampled if their sample rate is different to the playback rate");
|
||||
{
|
||||
for (const auto resampleRatio : { 0.1, 0.5, 2.0, 10.0 })
|
||||
{
|
||||
const auto length = static_cast<int> (spec.maximumBlockSize) * 2;
|
||||
const auto ramp = makeStereoRamp (length);
|
||||
|
||||
const auto resampled = [&]
|
||||
{
|
||||
AudioBuffer<float> original = ramp;
|
||||
MemoryAudioSource memorySource (original, false);
|
||||
ResamplingAudioSource resamplingSource (&memorySource, false, original.getNumChannels());
|
||||
|
||||
const auto finalSize = roundToInt (original.getNumSamples() / resampleRatio);
|
||||
resamplingSource.setResamplingRatio (resampleRatio);
|
||||
resamplingSource.prepareToPlay (finalSize, spec.sampleRate * resampleRatio);
|
||||
|
||||
AudioBuffer<float> result (original.getNumChannels(), finalSize);
|
||||
resamplingSource.getNextAudioBlock ({ &result, 0, result.getNumSamples() });
|
||||
|
||||
result.applyGain ((float) resampleRatio);
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { 0 },
|
||||
ramp,
|
||||
spec.sampleRate * resampleRatio,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
resampled);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Non-uniform convolutions work");
|
||||
{
|
||||
const auto ramp = makeRamp (static_cast<int> (spec.maximumBlockSize) * 8);
|
||||
|
||||
for (auto headSize : { spec.maximumBlockSize / 2, spec.maximumBlockSize, spec.maximumBlockSize * 9 })
|
||||
{
|
||||
testConvolution (spec,
|
||||
Convolution::NonUniform { static_cast<int> (headSize) },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
ramp);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Convolutions with latency work");
|
||||
{
|
||||
const auto ramp = makeRamp (static_cast<int> (spec.maximumBlockSize) * 8);
|
||||
using BlockSize = decltype (spec.maximumBlockSize);
|
||||
|
||||
for (auto latency : { static_cast<BlockSize> (0),
|
||||
spec.maximumBlockSize / 3,
|
||||
spec.maximumBlockSize,
|
||||
spec.maximumBlockSize * 2,
|
||||
static_cast<BlockSize> (spec.maximumBlockSize * 2.5) })
|
||||
{
|
||||
testConvolution (spec,
|
||||
Convolution::Latency { static_cast<int> (latency) },
|
||||
ramp,
|
||||
spec.sampleRate,
|
||||
Convolution::Stereo::yes,
|
||||
Convolution::Trim::yes,
|
||||
Convolution::Normalise::no,
|
||||
ramp);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ConvolutionTest convolutionUnitTest;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
1002
deps/juce/modules/juce_dsp/frequency/juce_FFT.cpp
vendored
Normal file
1002
deps/juce/modules/juce_dsp/frequency/juce_FFT.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
127
deps/juce/modules/juce_dsp/frequency/juce_FFT.h
vendored
Normal file
127
deps/juce/modules/juce_dsp/frequency/juce_FFT.h
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Performs a fast fourier transform.
|
||||
|
||||
This is only a simple low-footprint implementation and isn't tuned for speed - it may
|
||||
be useful for simple applications where one of the more complex FFT libraries would be
|
||||
overkill. (But in the future it may end up becoming optimised of course...)
|
||||
|
||||
The FFT class itself contains lookup tables, so there's some overhead in creating
|
||||
one, you should create and cache an FFT object for each size/direction of transform
|
||||
that you need, and re-use them to perform the actual operation.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
class JUCE_API FFT
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Initialises an object for performing forward and inverse FFT with the given size.
|
||||
The number of points the FFT will operate on will be 2 ^ order.
|
||||
*/
|
||||
FFT (int order);
|
||||
|
||||
/** Move constructor. */
|
||||
FFT (FFT&&) noexcept;
|
||||
|
||||
/** Move assignment operator. */
|
||||
FFT& operator= (FFT&&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~FFT();
|
||||
|
||||
//==============================================================================
|
||||
/** Performs an out-of-place FFT, either forward or inverse.
|
||||
The arrays must contain at least getSize() elements.
|
||||
*/
|
||||
void perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept;
|
||||
|
||||
/** Performs an in-place forward transform on a block of real data.
|
||||
|
||||
As the coefficients of the negative frequencies (frequencies higher than
|
||||
N/2 or pi) are the complex conjugate of their positive counterparts,
|
||||
it may not be necessary to calculate them for your particular application.
|
||||
You can use dontCalculateNegativeFrequencies to let the FFT
|
||||
engine know that you do not plan on using them. Note that this is only a
|
||||
hint: some FFT engines (currently only the Fallback engine), will still
|
||||
calculate the negative frequencies even if dontCalculateNegativeFrequencies
|
||||
is true.
|
||||
|
||||
The size of the array passed in must be 2 * getSize(), and the first half
|
||||
should contain your raw input sample data. On return, if
|
||||
dontCalculateNegativeFrequencies is false, the array will contain size
|
||||
complex real + imaginary parts data interleaved. If
|
||||
dontCalculateNegativeFrequencies is true, the array will contain at least
|
||||
(size / 2) + 1 complex numbers. Both outputs can be passed to
|
||||
performRealOnlyInverseTransform() in order to convert it back to reals.
|
||||
*/
|
||||
void performRealOnlyForwardTransform (float* inputOutputData,
|
||||
bool dontCalculateNegativeFrequencies = false) const noexcept;
|
||||
|
||||
/** Performs a reverse operation to data created in performRealOnlyForwardTransform().
|
||||
|
||||
Although performRealOnlyInverseTransform will only use the first ((size / 2) + 1)
|
||||
complex numbers, the size of the array passed in must still be 2 * getSize(), as some
|
||||
FFT engines require the extra space for the calculation. On return, the first half of the
|
||||
array will contain the reconstituted samples.
|
||||
*/
|
||||
void performRealOnlyInverseTransform (float* inputOutputData) const noexcept;
|
||||
|
||||
/** Takes an array and simply transforms it to the magnitude frequency response
|
||||
spectrum. This may be handy for things like frequency displays or analysis.
|
||||
The size of the array passed in must be 2 * getSize().
|
||||
*/
|
||||
void performFrequencyOnlyForwardTransform (float* inputOutputData) const noexcept;
|
||||
|
||||
/** Returns the number of data points that this FFT was created to work with. */
|
||||
int getSize() const noexcept { return size; }
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
/* internal */
|
||||
struct Instance;
|
||||
template <typename> struct EngineImpl;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Engine;
|
||||
|
||||
std::unique_ptr<Instance> engine;
|
||||
int size;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFT)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
214
deps/juce/modules/juce_dsp/frequency/juce_FFT_test.cpp
vendored
Normal file
214
deps/juce/modules/juce_dsp/frequency/juce_FFT_test.cpp
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
struct FFTUnitTest : public UnitTest
|
||||
{
|
||||
FFTUnitTest()
|
||||
: UnitTest ("FFT", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
static void fillRandom (Random& random, Complex<float>* buffer, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
buffer[i] = Complex<float> ((2.0f * random.nextFloat()) - 1.0f,
|
||||
(2.0f * random.nextFloat()) - 1.0f);
|
||||
}
|
||||
|
||||
static void fillRandom (Random& random, float* buffer, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
buffer[i] = (2.0f * random.nextFloat()) - 1.0f;
|
||||
}
|
||||
|
||||
static Complex<float> freqConvolution (const Complex<float>* in, float freq, size_t n)
|
||||
{
|
||||
Complex<float> sum (0.0, 0.0);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
sum += in[i] * exp (Complex<float> (0, static_cast<float> (i) * freq));
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void performReferenceFourier (const Complex<float>* in, Complex<float>* out,
|
||||
size_t n, bool reverse)
|
||||
{
|
||||
auto base_freq = static_cast<float> (((reverse ? 1.0 : -1.0) * MathConstants<double>::twoPi)
|
||||
/ static_cast<float> (n));
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
out[i] = freqConvolution (in, static_cast<float>(i) * base_freq, n);
|
||||
}
|
||||
|
||||
static void performReferenceFourier (const float* in, Complex<float>* out,
|
||||
size_t n, bool reverse)
|
||||
{
|
||||
HeapBlock<Complex<float>> buffer (n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
buffer.getData()[i] = Complex<float> (in[i], 0.0f);
|
||||
|
||||
float base_freq = static_cast<float> (((reverse ? 1.0 : -1.0) * MathConstants<double>::twoPi)
|
||||
/ static_cast<float> (n));
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
out[i] = freqConvolution (buffer.getData(), static_cast<float>(i) * base_freq, n);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
if (std::abs (a[i] - b[i]) > 1e-3f)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct RealTest
|
||||
{
|
||||
static void run (FFTUnitTest& u)
|
||||
{
|
||||
Random random (378272);
|
||||
|
||||
for (size_t order = 0; order <= 8; ++order)
|
||||
{
|
||||
auto n = (1u << order);
|
||||
|
||||
FFT fft ((int) order);
|
||||
|
||||
HeapBlock<float> input (n);
|
||||
HeapBlock<Complex<float>> reference (n), output (n);
|
||||
|
||||
fillRandom (random, input.getData(), n);
|
||||
performReferenceFourier (input.getData(), reference.getData(), n, false);
|
||||
|
||||
// fill only first half with real numbers
|
||||
zeromem (output.getData(), n * sizeof (Complex<float>));
|
||||
memcpy (reinterpret_cast<float*> (output.getData()), input.getData(), n * sizeof (float));
|
||||
|
||||
fft.performRealOnlyForwardTransform ((float*) output.getData());
|
||||
u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), n));
|
||||
|
||||
// fill only first half with real numbers
|
||||
zeromem (output.getData(), n * sizeof (Complex<float>));
|
||||
memcpy (reinterpret_cast<float*> (output.getData()), input.getData(), n * sizeof (float));
|
||||
|
||||
fft.performRealOnlyForwardTransform ((float*) output.getData(), true);
|
||||
std::fill (reference.getData() + ((n >> 1) + 1), reference.getData() + n, std::complex<float> (0.0f));
|
||||
u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), (n >> 1) + 1));
|
||||
|
||||
memcpy (output.getData(), reference.getData(), n * sizeof (Complex<float>));
|
||||
fft.performRealOnlyInverseTransform ((float*) output.getData());
|
||||
u.expect (checkArrayIsSimilar ((float*) output.getData(), input.getData(), n));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FrequencyOnlyTest
|
||||
{
|
||||
static void run(FFTUnitTest& u)
|
||||
{
|
||||
Random random (378272);
|
||||
for (size_t order = 0; order <= 8; ++order)
|
||||
{
|
||||
auto n = (1u << order);
|
||||
|
||||
FFT fft ((int) order);
|
||||
|
||||
HeapBlock<float> inout (n << 1), reference (n << 1);
|
||||
HeapBlock<Complex<float>> frequency (n);
|
||||
|
||||
fillRandom (random, inout.getData(), n);
|
||||
zeromem (reference.getData(), sizeof (float) * ((size_t) n << 1));
|
||||
performReferenceFourier (inout.getData(), frequency.getData(), n, false);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
reference.getData()[i] = std::abs (frequency.getData()[i]);
|
||||
|
||||
fft.performFrequencyOnlyForwardTransform (inout.getData());
|
||||
|
||||
u.expect (checkArrayIsSimilar (inout.getData(), reference.getData(), n));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ComplexTest
|
||||
{
|
||||
static void run(FFTUnitTest& u)
|
||||
{
|
||||
Random random (378272);
|
||||
|
||||
for (size_t order = 0; order <= 7; ++order)
|
||||
{
|
||||
auto n = (1u << order);
|
||||
|
||||
FFT fft ((int) order);
|
||||
|
||||
HeapBlock<Complex<float>> input (n), buffer (n), output (n), reference (n);
|
||||
|
||||
fillRandom (random, input.getData(), n);
|
||||
performReferenceFourier (input.getData(), reference.getData(), n, false);
|
||||
|
||||
memcpy (buffer.getData(), input.getData(), sizeof (Complex<float>) * n);
|
||||
fft.perform (buffer.getData(), output.getData(), false);
|
||||
|
||||
u.expect (checkArrayIsSimilar (output.getData(), reference.getData(), n));
|
||||
|
||||
memcpy (buffer.getData(), reference.getData(), sizeof (Complex<float>) * n);
|
||||
fft.perform (buffer.getData(), output.getData(), true);
|
||||
|
||||
|
||||
u.expect (checkArrayIsSimilar (output.getData(), input.getData(), n));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class TheTest>
|
||||
void runTestForAllTypes (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
TheTest::run (*this);
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
runTestForAllTypes<RealTest> ("Real input numbers Test");
|
||||
runTestForAllTypes<FrequencyOnlyTest> ("Frequency only Test");
|
||||
runTestForAllTypes<ComplexTest> ("Complex input numbers Test");
|
||||
}
|
||||
};
|
||||
|
||||
static FFTUnitTest fftUnitTest;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
196
deps/juce/modules/juce_dsp/frequency/juce_Windowing.cpp
vendored
Normal file
196
deps/juce/modules/juce_dsp/frequency/juce_Windowing.cpp
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 FloatType>
|
||||
static FloatType ncos (size_t order, size_t i, size_t size) noexcept
|
||||
{
|
||||
return std::cos (static_cast<FloatType> (order * i)
|
||||
* MathConstants<FloatType>::pi / static_cast<FloatType> (size - 1));
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
WindowingFunction<FloatType>::WindowingFunction (size_t size, WindowingMethod type, bool normalise, FloatType beta)
|
||||
{
|
||||
fillWindowingTables (size, type, normalise, beta);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
void WindowingFunction<FloatType>::fillWindowingTables (size_t size, WindowingMethod type,
|
||||
bool normalise, FloatType beta) noexcept
|
||||
{
|
||||
windowTable.resize (static_cast<int> (size));
|
||||
fillWindowingTables (windowTable.getRawDataPointer(), size, type, normalise, beta);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
void WindowingFunction<FloatType>::fillWindowingTables (FloatType* samples, size_t size,
|
||||
WindowingMethod type, bool normalise,
|
||||
FloatType beta) noexcept
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case rectangular:
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
samples[i] = static_cast<FloatType> (1);
|
||||
}
|
||||
break;
|
||||
|
||||
case triangular:
|
||||
{
|
||||
auto halfSlots = static_cast<FloatType> (0.5) * static_cast<FloatType> (size - 1);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
samples[i] = static_cast<FloatType> (1.0) - std::abs ((static_cast<FloatType> (i) - halfSlots) / halfSlots);
|
||||
}
|
||||
break;
|
||||
|
||||
case hann:
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto cos2 = ncos<FloatType> (2, i, size);
|
||||
samples[i] = static_cast<FloatType> (0.5 - 0.5 * cos2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case hamming:
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto cos2 = ncos<FloatType> (2, i, size);
|
||||
samples[i] = static_cast<FloatType> (0.54 - 0.46 * cos2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case blackman:
|
||||
{
|
||||
constexpr FloatType alpha = 0.16f;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto cos2 = ncos<FloatType> (2, i, size);
|
||||
auto cos4 = ncos<FloatType> (4, i, size);
|
||||
|
||||
samples[i] = static_cast<FloatType> (0.5 * (1 - alpha) - 0.5 * cos2 + 0.5 * alpha * cos4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case blackmanHarris:
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto cos2 = ncos<FloatType> (2, i, size);
|
||||
auto cos4 = ncos<FloatType> (4, i, size);
|
||||
auto cos6 = ncos<FloatType> (6, i, size);
|
||||
|
||||
samples[i] = static_cast<FloatType> (0.35875 - 0.48829 * cos2 + 0.14128 * cos4 - 0.01168 * cos6);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case flatTop:
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto cos2 = ncos<FloatType> (2, i, size);
|
||||
auto cos4 = ncos<FloatType> (4, i, size);
|
||||
auto cos6 = ncos<FloatType> (6, i, size);
|
||||
auto cos8 = ncos<FloatType> (8, i, size);
|
||||
|
||||
samples[i] = static_cast<FloatType> (1.0 - 1.93 * cos2 + 1.29 * cos4 - 0.388 * cos6 + 0.028 * cos8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kaiser:
|
||||
{
|
||||
const double factor = 1.0 / SpecialFunctions::besselI0 (beta);
|
||||
const auto doubleSize = (double) size;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
samples[i] = static_cast<FloatType> (SpecialFunctions::besselI0 (beta * std::sqrt (1.0 - std::pow (((double) i - 0.5 * (doubleSize - 1.0))
|
||||
/ ( 0.5 * (doubleSize - 1.0)), 2.0)))
|
||||
* factor);
|
||||
}
|
||||
break;
|
||||
|
||||
case numWindowingMethods:
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
// DC frequency amplitude must be one
|
||||
if (normalise)
|
||||
{
|
||||
FloatType sum (0);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
sum += samples[i];
|
||||
|
||||
auto factor = static_cast<FloatType> (size) / sum;
|
||||
|
||||
FloatVectorOperations::multiply (samples, factor, static_cast<int> (size));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
void WindowingFunction<FloatType>::multiplyWithWindowingTable (FloatType* samples, size_t size) noexcept
|
||||
{
|
||||
FloatVectorOperations::multiply (samples, windowTable.getRawDataPointer(), jmin (static_cast<int> (size), windowTable.size()));
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
const char* WindowingFunction<FloatType>::getWindowingMethodName (WindowingMethod type) noexcept
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case rectangular: return "Rectangular";
|
||||
case triangular: return "Triangular";
|
||||
case hann: return "Hann";
|
||||
case hamming: return "Hamming";
|
||||
case blackman: return "Blackman";
|
||||
case blackmanHarris: return "Blackman-Harris";
|
||||
case flatTop: return "Flat Top";
|
||||
case kaiser: return "Kaiser";
|
||||
case numWindowingMethods:
|
||||
default: jassertfalse; return "";
|
||||
}
|
||||
}
|
||||
|
||||
template class WindowingFunction<float>;
|
||||
template class WindowingFunction<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
111
deps/juce/modules/juce_dsp/frequency/juce_Windowing.h
vendored
Normal file
111
deps/juce/modules/juce_dsp/frequency/juce_Windowing.h
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A class which provides multiple windowing functions useful for filter design
|
||||
and spectrum analyzers.
|
||||
|
||||
The different functions provided here can be used by creating either a
|
||||
WindowingFunction object, or a static function to fill an array with the
|
||||
windowing method samples.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class JUCE_API WindowingFunction
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The windowing methods available. */
|
||||
enum WindowingMethod
|
||||
{
|
||||
rectangular = 0,
|
||||
triangular,
|
||||
hann,
|
||||
hamming,
|
||||
blackman,
|
||||
blackmanHarris,
|
||||
flatTop,
|
||||
kaiser,
|
||||
numWindowingMethods
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This constructor automatically fills a buffer of the specified size using
|
||||
the fillWindowingTables function and the specified arguments.
|
||||
|
||||
@see fillWindowingTables
|
||||
*/
|
||||
WindowingFunction (size_t size, WindowingMethod,
|
||||
bool normalise = true, FloatType beta = 0);
|
||||
|
||||
//==============================================================================
|
||||
/** Fills the content of the object array with a given windowing method table.
|
||||
|
||||
@param size the size of the destination buffer allocated in the object
|
||||
@param type the type of windowing method being used
|
||||
@param normalise if the result must be normalised, creating a DC amplitude
|
||||
response of one
|
||||
@param beta an optional argument useful only for Kaiser's method
|
||||
which must be positive and sets the properties of the
|
||||
method (bandwidth and attenuation increases with beta)
|
||||
*/
|
||||
void fillWindowingTables (size_t size, WindowingMethod type,
|
||||
bool normalise = true, FloatType beta = 0) noexcept;
|
||||
|
||||
/** Fills the content of an array with a given windowing method table.
|
||||
|
||||
@param samples the destination buffer pointer
|
||||
@param size the size of the destination buffer allocated in the object
|
||||
@param normalise if the result must be normalised, creating a DC amplitude
|
||||
response of one
|
||||
@param beta an optional argument useful only for Kaiser's method,
|
||||
which must be positive and sets the properties of the
|
||||
method (bandwidth and attenuation increases with beta)
|
||||
*/
|
||||
static void fillWindowingTables (FloatType* samples, size_t size, WindowingMethod,
|
||||
bool normalise = true, FloatType beta = 0) noexcept;
|
||||
|
||||
/** Multiplies the content of a buffer with the given window. */
|
||||
void multiplyWithWindowingTable (FloatType* samples, size_t size) noexcept;
|
||||
|
||||
/** Returns the name of a given windowing method. */
|
||||
static const char* getWindowingMethodName (WindowingMethod) noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<FloatType> windowTable;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowingFunction)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
109
deps/juce/modules/juce_dsp/juce_dsp.cpp
vendored
Normal file
109
deps/juce/modules/juce_dsp/juce_dsp.cpp
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_DSP_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#include "juce_dsp.h"
|
||||
|
||||
#ifndef JUCE_USE_VDSP_FRAMEWORK
|
||||
#define JUCE_USE_VDSP_FRAMEWORK 1
|
||||
#endif
|
||||
|
||||
#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK
|
||||
#include <Accelerate/Accelerate.h>
|
||||
#else
|
||||
#undef JUCE_USE_VDSP_FRAMEWORK
|
||||
#endif
|
||||
|
||||
#if JUCE_DSP_USE_INTEL_MKL
|
||||
#include <mkl_dfti.h>
|
||||
#endif
|
||||
|
||||
#if _IPP_SEQUENTIAL_STATIC || _IPP_SEQUENTIAL_DYNAMIC || _IPP_PARALLEL_STATIC || _IPP_PARALLEL_DYNAMIC
|
||||
#include <ippcore.h>
|
||||
#include <ipps.h>
|
||||
#define JUCE_IPP_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#include "processors/juce_FIRFilter.cpp"
|
||||
#include "processors/juce_IIRFilter.cpp"
|
||||
#include "processors/juce_FirstOrderTPTFilter.cpp"
|
||||
#include "processors/juce_Panner.cpp"
|
||||
#include "processors/juce_Oversampling.cpp"
|
||||
#include "processors/juce_BallisticsFilter.cpp"
|
||||
#include "processors/juce_LinkwitzRileyFilter.cpp"
|
||||
#include "processors/juce_DelayLine.cpp"
|
||||
#include "processors/juce_DryWetMixer.cpp"
|
||||
#include "processors/juce_StateVariableTPTFilter.cpp"
|
||||
#include "maths/juce_SpecialFunctions.cpp"
|
||||
#include "maths/juce_Matrix.cpp"
|
||||
#include "maths/juce_LookupTable.cpp"
|
||||
#include "frequency/juce_FFT.cpp"
|
||||
#include "frequency/juce_Convolution.cpp"
|
||||
#include "frequency/juce_Windowing.cpp"
|
||||
#include "filter_design/juce_FilterDesign.cpp"
|
||||
#include "widgets/juce_LadderFilter.cpp"
|
||||
#include "widgets/juce_Compressor.cpp"
|
||||
#include "widgets/juce_NoiseGate.cpp"
|
||||
#include "widgets/juce_Limiter.cpp"
|
||||
#include "widgets/juce_Phaser.cpp"
|
||||
#include "widgets/juce_Chorus.cpp"
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_X64) || defined(_X86_) || defined(_M_IX86)
|
||||
#ifdef __AVX2__
|
||||
#include "native/juce_avx_SIMDNativeOps.cpp"
|
||||
#else
|
||||
#include "native/juce_sse_SIMDNativeOps.cpp"
|
||||
#endif
|
||||
#elif defined(__arm__) || defined(_M_ARM) || defined (__arm64__) || defined (__aarch64__)
|
||||
#include "native/juce_neon_SIMDNativeOps.cpp"
|
||||
#else
|
||||
#error "SIMD register support not implemented for this platform"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
#include "maths/juce_Matrix_test.cpp"
|
||||
#include "maths/juce_LogRampedValue_test.cpp"
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
#include "containers/juce_SIMDRegister_test.cpp"
|
||||
#endif
|
||||
|
||||
#include "containers/juce_AudioBlock_test.cpp"
|
||||
#include "containers/juce_FixedSizeFunction_test.cpp"
|
||||
#include "frequency/juce_Convolution_test.cpp"
|
||||
#include "frequency/juce_FFT_test.cpp"
|
||||
#include "processors/juce_FIRFilter_test.cpp"
|
||||
#include "processors/juce_ProcessorChain_test.cpp"
|
||||
#endif
|
||||
277
deps/juce/modules/juce_dsp/juce_dsp.h
vendored
Normal file
277
deps/juce/modules/juce_dsp/juce_dsp.h
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_dsp
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE DSP classes
|
||||
description: Classes for audio buffer manipulation, digital audio processing, filtering, oversampling, fast math functions etc.
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_audio_formats
|
||||
OSXFrameworks: Accelerate
|
||||
iOSFrameworks: Accelerate
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#define JUCE_DSP_H_INCLUDED
|
||||
|
||||
#include <juce_audio_basics/juce_audio_basics.h>
|
||||
#include <juce_audio_formats/juce_audio_formats.h>
|
||||
|
||||
#if defined(_M_X64) || defined(__amd64__) || defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP == 2)
|
||||
|
||||
#if defined(_M_X64) || defined(__amd64__)
|
||||
#ifndef __SSE2__
|
||||
#define __SSE2__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JUCE_USE_SIMD
|
||||
#define JUCE_USE_SIMD 1
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
#elif defined (__ARM_NEON__) || defined (__ARM_NEON) || defined (__arm64__) || defined (__aarch64__)
|
||||
|
||||
#ifndef JUCE_USE_SIMD
|
||||
#define JUCE_USE_SIMD 1
|
||||
#endif
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
#else
|
||||
|
||||
// No SIMD Support
|
||||
#ifndef JUCE_USE_SIMD
|
||||
#define JUCE_USE_SIMD 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef JUCE_VECTOR_CALLTYPE
|
||||
// __vectorcall does not work on 64-bit due to internal compiler error in
|
||||
// release mode in both VS2015 and VS2017. Re-enable when Microsoft fixes this
|
||||
#if _MSC_VER && JUCE_USE_SIMD && ! (defined(_M_X64) || defined(__amd64__))
|
||||
#define JUCE_VECTOR_CALLTYPE __vectorcall
|
||||
#else
|
||||
#define JUCE_VECTOR_CALLTYPE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <complex>
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_ASSERTION_FIRFILTER
|
||||
|
||||
When this flag is enabled, an assertion will be generated during the
|
||||
execution of DEBUG configurations if you use a FIRFilter class to process
|
||||
FIRCoefficients with a size higher than 128, to tell you that's it would be
|
||||
more efficient to use the Convolution class instead. It is enabled by
|
||||
default, but you may want to disable it if you really want to process such
|
||||
a filter in the time domain.
|
||||
*/
|
||||
#ifndef JUCE_ASSERTION_FIRFILTER
|
||||
#define JUCE_ASSERTION_FIRFILTER 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DSP_USE_INTEL_MKL
|
||||
|
||||
If this flag is set, then JUCE will use Intel's MKL for JUCE's FFT and
|
||||
convolution classes.
|
||||
|
||||
If you're using the Projucer's Visual Studio exporter, you should also set
|
||||
the "Use MKL Library (oneAPI)" option in the exporter settings to
|
||||
"Sequential" or "Parallel". If you're not using the Visual Studio exporter,
|
||||
the folder containing the mkl_dfti.h header must be in your header search
|
||||
paths, and you must link against all the necessary MKL libraries.
|
||||
*/
|
||||
#ifndef JUCE_DSP_USE_INTEL_MKL
|
||||
#define JUCE_DSP_USE_INTEL_MKL 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DSP_USE_SHARED_FFTW
|
||||
|
||||
If this flag is set, then JUCE will search for the fftw shared libraries
|
||||
at runtime and use the library for JUCE's FFT and convolution classes.
|
||||
|
||||
If the library is not found, then JUCE's fallback FFT routines will be used.
|
||||
|
||||
This is especially useful on linux as fftw often comes pre-installed on
|
||||
popular linux distros.
|
||||
|
||||
You must respect the FFTW license when enabling this option.
|
||||
*/
|
||||
#ifndef JUCE_DSP_USE_SHARED_FFTW
|
||||
#define JUCE_DSP_USE_SHARED_FFTW 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DSP_USE_STATIC_FFTW
|
||||
|
||||
If this flag is set, then JUCE will use the statically linked fftw libraries
|
||||
for JUCE's FFT and convolution classes.
|
||||
|
||||
You must add the fftw header/library folder to the extra header/library search
|
||||
paths of your JUCE project. You also need to add the fftw library itself
|
||||
to the extra libraries supplied to your JUCE project during linking.
|
||||
|
||||
You must respect the FFTW license when enabling this option.
|
||||
*/
|
||||
#ifndef JUCE_DSP_USE_STATIC_FFTW
|
||||
#define JUCE_DSP_USE_STATIC_FFTW 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
|
||||
Enables code in the dsp module to avoid floating point denormals during the
|
||||
processing of some of the dsp module's filters.
|
||||
|
||||
Enabling this will add a slight performance overhead to the DSP module's
|
||||
filters and algorithms. If your audio app already disables denormals altogether
|
||||
(for example, by using the ScopedNoDenormals class or the
|
||||
FloatVectorOperations::disableDenormalisedNumberSupport method), then you
|
||||
can safely disable this flag to shave off a few cpu cycles from the DSP module's
|
||||
filters and algorithms.
|
||||
*/
|
||||
#ifndef JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
#define JUCE_DSP_ENABLE_SNAP_TO_ZERO 1
|
||||
#endif
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#undef Complex // apparently some C libraries actually define these symbols (!)
|
||||
#undef Factor
|
||||
#undef check
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
template <typename Type>
|
||||
using Complex = std::complex<Type>;
|
||||
|
||||
//==============================================================================
|
||||
namespace util
|
||||
{
|
||||
/** Use this function to prevent denormals on intel CPUs.
|
||||
This function will work with both primitives and simple containers.
|
||||
*/
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
inline void snapToZero (float& x) noexcept { JUCE_SNAP_TO_ZERO (x); }
|
||||
#ifndef DOXYGEN
|
||||
inline void snapToZero (double& x) noexcept { JUCE_SNAP_TO_ZERO (x); }
|
||||
inline void snapToZero (long double& x) noexcept { JUCE_SNAP_TO_ZERO (x); }
|
||||
#endif
|
||||
#else
|
||||
inline void snapToZero (float& x) noexcept { ignoreUnused (x); }
|
||||
#ifndef DOXYGEN
|
||||
inline void snapToZero (double& x) noexcept { ignoreUnused (x); }
|
||||
inline void snapToZero (long double& x) noexcept { ignoreUnused (x); }
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_USE_SIMD
|
||||
#include "native/juce_fallback_SIMDNativeOps.h"
|
||||
|
||||
// include the correct native file for this build target CPU
|
||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_X64) || defined(_X86_) || defined(_M_IX86)
|
||||
#ifdef __AVX2__
|
||||
#include "native/juce_avx_SIMDNativeOps.h"
|
||||
#else
|
||||
#include "native/juce_sse_SIMDNativeOps.h"
|
||||
#endif
|
||||
#elif defined(__arm__) || defined(_M_ARM) || defined (__arm64__) || defined (__aarch64__)
|
||||
#include "native/juce_neon_SIMDNativeOps.h"
|
||||
#else
|
||||
#error "SIMD register support not implemented for this platform"
|
||||
#endif
|
||||
|
||||
#include "containers/juce_SIMDRegister.h"
|
||||
#endif
|
||||
|
||||
#include "maths/juce_SpecialFunctions.h"
|
||||
#include "maths/juce_Matrix.h"
|
||||
#include "maths/juce_Phase.h"
|
||||
#include "maths/juce_Polynomial.h"
|
||||
#include "maths/juce_FastMathApproximations.h"
|
||||
#include "maths/juce_LookupTable.h"
|
||||
#include "maths/juce_LogRampedValue.h"
|
||||
#include "containers/juce_AudioBlock.h"
|
||||
#include "containers/juce_FixedSizeFunction.h"
|
||||
#include "processors/juce_ProcessContext.h"
|
||||
#include "processors/juce_ProcessorWrapper.h"
|
||||
#include "processors/juce_ProcessorChain.h"
|
||||
#include "processors/juce_ProcessorDuplicator.h"
|
||||
#include "processors/juce_IIRFilter.h"
|
||||
#include "processors/juce_FIRFilter.h"
|
||||
#include "processors/juce_StateVariableFilter.h"
|
||||
#include "processors/juce_FirstOrderTPTFilter.h"
|
||||
#include "processors/juce_Panner.h"
|
||||
#include "processors/juce_DelayLine.h"
|
||||
#include "processors/juce_Oversampling.h"
|
||||
#include "processors/juce_BallisticsFilter.h"
|
||||
#include "processors/juce_LinkwitzRileyFilter.h"
|
||||
#include "processors/juce_DryWetMixer.h"
|
||||
#include "processors/juce_StateVariableTPTFilter.h"
|
||||
#include "frequency/juce_FFT.h"
|
||||
#include "frequency/juce_Convolution.h"
|
||||
#include "frequency/juce_Windowing.h"
|
||||
#include "filter_design/juce_FilterDesign.h"
|
||||
#include "widgets/juce_Reverb.h"
|
||||
#include "widgets/juce_Bias.h"
|
||||
#include "widgets/juce_Gain.h"
|
||||
#include "widgets/juce_WaveShaper.h"
|
||||
#include "widgets/juce_Oscillator.h"
|
||||
#include "widgets/juce_LadderFilter.h"
|
||||
#include "widgets/juce_Compressor.h"
|
||||
#include "widgets/juce_NoiseGate.h"
|
||||
#include "widgets/juce_Limiter.h"
|
||||
#include "widgets/juce_Phaser.h"
|
||||
#include "widgets/juce_Chorus.h"
|
||||
26
deps/juce/modules/juce_dsp/juce_dsp.mm
vendored
Normal file
26
deps/juce/modules/juce_dsp/juce_dsp.mm
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "juce_dsp.cpp"
|
||||
264
deps/juce/modules/juce_dsp/maths/juce_FastMathApproximations.h
vendored
Normal file
264
deps/juce/modules/juce_dsp/maths/juce_FastMathApproximations.h
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
This class contains various fast mathematical function approximations.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct FastMathApproximations
|
||||
{
|
||||
/** Provides a fast approximation of the function cosh(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -5 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType cosh (FloatType x) noexcept
|
||||
{
|
||||
auto x2 = x * x;
|
||||
auto numerator = -(39251520 + x2 * (18471600 + x2 * (1075032 + 14615 * x2)));
|
||||
auto denominator = -39251520 + x2 * (1154160 + x2 * (-16632 + 127 * x2));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function cosh(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -5 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void cosh (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::cosh (values[i]);
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function sinh(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -5 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType sinh (FloatType x) noexcept
|
||||
{
|
||||
auto x2 = x * x;
|
||||
auto numerator = -x * (11511339840 + x2 * (1640635920 + x2 * (52785432 + x2 * 479249)));
|
||||
auto denominator = -11511339840 + x2 * (277920720 + x2 * (-3177720 + x2 * 18361));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function sinh(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -5 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void sinh (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::sinh (values[i]);
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function tanh(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -5 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType tanh (FloatType x) noexcept
|
||||
{
|
||||
auto x2 = x * x;
|
||||
auto numerator = x * (135135 + x2 * (17325 + x2 * (378 + x2)));
|
||||
auto denominator = 135135 + x2 * (62370 + x2 * (3150 + 28 * x2));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function tanh(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -5 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void tanh (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::tanh (values[i]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Provides a fast approximation of the function cos(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -pi and +pi for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType cos (FloatType x) noexcept
|
||||
{
|
||||
auto x2 = x * x;
|
||||
auto numerator = -(-39251520 + x2 * (18471600 + x2 * (-1075032 + 14615 * x2)));
|
||||
auto denominator = 39251520 + x2 * (1154160 + x2 * (16632 + x2 * 127));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function cos(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -pi and +pi for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void cos (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::cos (values[i]);
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function sin(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -pi and +pi for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType sin (FloatType x) noexcept
|
||||
{
|
||||
auto x2 = x * x;
|
||||
auto numerator = -x * (-11511339840 + x2 * (1640635920 + x2 * (-52785432 + x2 * 479249)));
|
||||
auto denominator = 11511339840 + x2 * (277920720 + x2 * (3177720 + x2 * 18361));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function sin(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -pi and +pi for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void sin (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::sin (values[i]);
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function tan(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -pi/2 and +pi/2 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType tan (FloatType x) noexcept
|
||||
{
|
||||
auto x2 = x * x;
|
||||
auto numerator = x * (-135135 + x2 * (17325 + x2 * (-378 + x2)));
|
||||
auto denominator = -135135 + x2 * (62370 + x2 * (-3150 + 28 * x2));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function tan(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -pi/2 and +pi/2 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void tan (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::tan (values[i]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Provides a fast approximation of the function exp(x) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -6 and +4 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType exp (FloatType x) noexcept
|
||||
{
|
||||
auto numerator = 1680 + x * (840 + x * (180 + x * (20 + x)));
|
||||
auto denominator = 1680 + x *(-840 + x * (180 + x * (-20 + x)));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function exp(x) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -6 and +4 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void exp (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::exp (values[i]);
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function log(x+1) using a Pade approximant
|
||||
continued fraction, calculated sample by sample.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -0.8 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static FloatType logNPlusOne (FloatType x) noexcept
|
||||
{
|
||||
auto numerator = x * (7560 + x * (15120 + x * (9870 + x * (2310 + x * 137))));
|
||||
auto denominator = 7560 + x * (18900 + x * (16800 + x * (6300 + x * (900 + 30 * x))));
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** Provides a fast approximation of the function log(x+1) using a Pade approximant
|
||||
continued fraction, calculated on a whole buffer.
|
||||
|
||||
Note: This is an approximation which works on a limited range. You are
|
||||
advised to use input values only between -0.8 and +5 for limiting the error.
|
||||
*/
|
||||
template <typename FloatType>
|
||||
static void logNPlusOne (FloatType* values, size_t numValues) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numValues; ++i)
|
||||
values[i] = FastMathApproximations::logNPlusOne (values[i]);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
189
deps/juce/modules/juce_dsp/maths/juce_LogRampedValue.h
vendored
Normal file
189
deps/juce/modules/juce_dsp/maths/juce_LogRampedValue.h
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Utility class for logarithmically smoothed linear values.
|
||||
|
||||
Logarithmically smoothed values can be more relevant than linear ones for
|
||||
specific cases such as algorithm change smoothing, using two of them in
|
||||
opposite directions.
|
||||
|
||||
The gradient of the logarithmic/exponential slope can be configured by
|
||||
calling LogRampedValue::setLogParameters.
|
||||
|
||||
@see SmoothedValue
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class LogRampedValue : public SmoothedValueBase <LogRampedValue <FloatType>>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
LogRampedValue() = default;
|
||||
|
||||
/** Constructor. */
|
||||
LogRampedValue (FloatType initialValue) noexcept
|
||||
{
|
||||
// Visual Studio can't handle base class initialisation with CRTP
|
||||
this->currentValue = initialValue;
|
||||
this->target = initialValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the behaviour of the log ramp.
|
||||
@param midPointAmplitudedB Sets the amplitude of the mid point in
|
||||
decibels, with the target value at 0 dB
|
||||
and the initial value at -inf dB
|
||||
@param rateOfChangeShouldIncrease If true then the ramp starts shallow
|
||||
and gets progressively steeper, if false
|
||||
then the ramp is initially steep and
|
||||
flattens out as you approach the target
|
||||
value
|
||||
*/
|
||||
void setLogParameters (FloatType midPointAmplitudedB, bool rateOfChangeShouldIncrease) noexcept
|
||||
{
|
||||
jassert (midPointAmplitudedB < (FloatType) 0.0);
|
||||
B = Decibels::decibelsToGain (midPointAmplitudedB);
|
||||
|
||||
increasingRateOfChange = rateOfChangeShouldIncrease;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reset to a new sample rate and ramp length.
|
||||
@param sampleRate The sample rate
|
||||
@param rampLengthInSeconds The duration of the ramp in seconds
|
||||
*/
|
||||
void reset (double sampleRate, double rampLengthInSeconds) noexcept
|
||||
{
|
||||
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
|
||||
reset ((int) std::floor (rampLengthInSeconds * sampleRate));
|
||||
}
|
||||
|
||||
/** Set a new ramp length directly in samples.
|
||||
@param numSteps The number of samples over which the ramp should be active
|
||||
*/
|
||||
void reset (int numSteps) noexcept
|
||||
{
|
||||
stepsToTarget = numSteps;
|
||||
|
||||
this->setCurrentAndTargetValue (this->target);
|
||||
|
||||
updateRampParameters();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Set a new target value.
|
||||
|
||||
@param newValue The new target value
|
||||
*/
|
||||
void setTargetValue (FloatType newValue) noexcept
|
||||
{
|
||||
if (newValue == this->target)
|
||||
return;
|
||||
|
||||
if (stepsToTarget <= 0)
|
||||
{
|
||||
this->setCurrentAndTargetValue (newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
this->target = newValue;
|
||||
this->countdown = stepsToTarget;
|
||||
source = this->currentValue;
|
||||
|
||||
updateRampParameters();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compute the next value.
|
||||
@returns Smoothed value
|
||||
*/
|
||||
FloatType getNextValue() noexcept
|
||||
{
|
||||
if (! this->isSmoothing())
|
||||
return this->target;
|
||||
|
||||
--(this->countdown);
|
||||
|
||||
temp *= r; temp += d;
|
||||
this->currentValue = jmap (temp, source, this->target);
|
||||
|
||||
return this->currentValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Skip the next numSamples samples.
|
||||
|
||||
This is identical to calling getNextValue numSamples times.
|
||||
@see getNextValue
|
||||
*/
|
||||
FloatType skip (int numSamples) noexcept
|
||||
{
|
||||
if (numSamples >= this->countdown)
|
||||
{
|
||||
this->setCurrentAndTargetValue (this->target);
|
||||
return this->target;
|
||||
}
|
||||
|
||||
this->countdown -= numSamples;
|
||||
|
||||
auto rN = (FloatType) std::pow (r, numSamples);
|
||||
temp *= rN;
|
||||
temp += d * (rN - (FloatType) 1) / (r - (FloatType) 1);
|
||||
|
||||
this->currentValue = jmap (temp, source, this->target);
|
||||
return this->currentValue;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void updateRampParameters()
|
||||
{
|
||||
auto D = increasingRateOfChange ? B : (FloatType) 1 - B;
|
||||
auto base = ((FloatType) 1 / D) - (FloatType) 1;
|
||||
r = std::pow (base, (FloatType) 2 / (FloatType) stepsToTarget);
|
||||
auto rN = std::pow (r, (FloatType) stepsToTarget);
|
||||
d = (r - (FloatType) 1) / (rN - (FloatType) 1);
|
||||
temp = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool increasingRateOfChange = true;
|
||||
FloatType B = Decibels::decibelsToGain ((FloatType) -40);
|
||||
|
||||
int stepsToTarget = 0;
|
||||
FloatType temp = 0, source = 0, r = 0, d = 1;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
96
deps/juce/modules/juce_dsp/maths/juce_LogRampedValue_test.cpp
vendored
Normal file
96
deps/juce/modules/juce_dsp/maths/juce_LogRampedValue_test.cpp
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
static CommonSmoothedValueTests <LogRampedValue <float>> commonLogRampedValueTests;
|
||||
|
||||
class LogRampedValueTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
LogRampedValueTests()
|
||||
: UnitTest ("LogRampedValueTests", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Curve");
|
||||
{
|
||||
Array<double> levels = { -0.12243, -1.21245, -12.2342, -22.4683, -30.0, -61.18753 };
|
||||
|
||||
for (auto level : levels)
|
||||
{
|
||||
Array<Range<double>> ranges = { Range<double> (0.0, 1.0),
|
||||
Range<double> (-2.345, 0.0),
|
||||
Range<double> (-2.63, 3.56),
|
||||
Range<double> (3.3, -0.2) };
|
||||
|
||||
for (auto range : ranges)
|
||||
{
|
||||
LogRampedValue<double> slowStart { range.getStart() } , fastStart { range.getEnd() };
|
||||
|
||||
auto numSamples = 12;
|
||||
slowStart.reset (numSamples);
|
||||
fastStart.reset (numSamples);
|
||||
|
||||
slowStart.setLogParameters (level, true);
|
||||
fastStart.setLogParameters (level, false);
|
||||
|
||||
slowStart.setTargetValue (range.getEnd());
|
||||
fastStart.setTargetValue (range.getStart());
|
||||
|
||||
AudioBuffer<double> results (2, numSamples + 1);
|
||||
|
||||
results.setSample (0, 0, slowStart.getCurrentValue());
|
||||
results.setSample (1, 0, fastStart.getCurrentValue());
|
||||
|
||||
for (int i = 1; i < results.getNumSamples(); ++i)
|
||||
{
|
||||
results.setSample (0, i, slowStart.getNextValue());
|
||||
results.setSample (1, i, fastStart.getNextValue());
|
||||
}
|
||||
|
||||
for (int i = 0; i < results.getNumSamples(); ++i)
|
||||
expectWithinAbsoluteError (results.getSample (0, i),
|
||||
results.getSample (1, results.getNumSamples() - (i + 1)),
|
||||
1.0e-7);
|
||||
|
||||
auto expectedMidpoint = range.getStart() + (range.getLength() * Decibels::decibelsToGain (level));
|
||||
expectWithinAbsoluteError (results.getSample (0, numSamples / 2),
|
||||
expectedMidpoint,
|
||||
1.0e-7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static LogRampedValueTests LogRampedValueTests;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
156
deps/juce/modules/juce_dsp/maths/juce_LookupTable.cpp
vendored
Normal file
156
deps/juce/modules/juce_dsp/maths/juce_LookupTable.cpp
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 FloatType>
|
||||
LookupTable<FloatType>::LookupTable()
|
||||
{
|
||||
data.resize (1);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
LookupTable<FloatType>::LookupTable (const std::function<FloatType (size_t)>& functionToApproximate,
|
||||
size_t numPointsToUse)
|
||||
{
|
||||
initialise (functionToApproximate, numPointsToUse);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename FloatType>
|
||||
void LookupTable<FloatType>::initialise (const std::function<FloatType (size_t)>& functionToApproximate,
|
||||
size_t numPointsToUse)
|
||||
{
|
||||
data.resize (static_cast<int> (getRequiredBufferSize (numPointsToUse)));
|
||||
|
||||
for (size_t i = 0; i < numPointsToUse; ++i)
|
||||
{
|
||||
auto value = functionToApproximate (i);
|
||||
|
||||
jassert (! std::isnan (value));
|
||||
jassert (! std::isinf (value));
|
||||
// Make sure functionToApproximate returns a sensible value for the entire specified range.
|
||||
// E.g., this won't work for zero: [] (size_t i) { return 1.0f / i; }
|
||||
|
||||
data.getReference (static_cast<int> (i)) = value;
|
||||
}
|
||||
|
||||
prepare();
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
void LookupTable<FloatType>::prepare() noexcept
|
||||
{
|
||||
auto guardIndex = static_cast<int> (getGuardIndex());
|
||||
data.getReference (guardIndex) = data.getUnchecked (guardIndex - 1);
|
||||
}
|
||||
|
||||
template <typename FloatType>
|
||||
void LookupTableTransform<FloatType>::initialise (const std::function<FloatType (FloatType)>& functionToApproximate,
|
||||
FloatType minInputValueToUse,
|
||||
FloatType maxInputValueToUse,
|
||||
size_t numPoints)
|
||||
{
|
||||
jassert (maxInputValueToUse > minInputValueToUse);
|
||||
|
||||
minInputValue = minInputValueToUse;
|
||||
maxInputValue = maxInputValueToUse;
|
||||
scaler = FloatType (numPoints - 1) / (maxInputValueToUse - minInputValueToUse);
|
||||
offset = -minInputValueToUse * scaler;
|
||||
|
||||
const auto initFn = [functionToApproximate, minInputValueToUse, maxInputValueToUse, numPoints] (size_t i)
|
||||
{
|
||||
return functionToApproximate (
|
||||
jlimit (
|
||||
minInputValueToUse, maxInputValueToUse,
|
||||
jmap (FloatType (i), FloatType (0), FloatType (numPoints - 1), minInputValueToUse, maxInputValueToUse))
|
||||
);
|
||||
};
|
||||
|
||||
lookupTable.initialise (initFn, numPoints);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename FloatType>
|
||||
double LookupTableTransform<FloatType>::calculateMaxRelativeError (const std::function<FloatType (FloatType)>& functionToApproximate,
|
||||
FloatType minInputValue,
|
||||
FloatType maxInputValue,
|
||||
size_t numPoints,
|
||||
size_t numTestPoints)
|
||||
{
|
||||
jassert (maxInputValue > minInputValue);
|
||||
|
||||
if (numTestPoints == 0)
|
||||
numTestPoints = 100 * numPoints; // use default
|
||||
|
||||
LookupTableTransform transform (functionToApproximate, minInputValue, maxInputValue, numPoints);
|
||||
|
||||
double maxError = 0;
|
||||
|
||||
for (size_t i = 0; i < numTestPoints; ++i)
|
||||
{
|
||||
auto inputValue = jmap (FloatType (i), FloatType (0), FloatType (numTestPoints - 1), minInputValue, maxInputValue);
|
||||
auto approximatedOutputValue = transform.processSample (inputValue);
|
||||
auto referenceOutputValue = functionToApproximate (inputValue);
|
||||
|
||||
maxError = jmax (maxError, calculateRelativeDifference ((double) referenceOutputValue, (double) approximatedOutputValue));
|
||||
}
|
||||
|
||||
return maxError;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename FloatType>
|
||||
double LookupTableTransform<FloatType>::calculateRelativeDifference (double x, double y) noexcept
|
||||
{
|
||||
static const auto eps = std::numeric_limits<double>::min();
|
||||
|
||||
auto absX = std::abs (x);
|
||||
auto absY = std::abs (y);
|
||||
auto absDiff = std::abs (x - y);
|
||||
|
||||
if (absX < eps)
|
||||
{
|
||||
if (absY >= eps)
|
||||
return absDiff / absY;
|
||||
|
||||
return absDiff; // return the absolute error if both numbers are too close to zero
|
||||
}
|
||||
|
||||
return absDiff / std::min (absX, absY);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class LookupTable<float>;
|
||||
template class LookupTable<double>;
|
||||
|
||||
template class LookupTableTransform<float>;
|
||||
template class LookupTableTransform<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
330
deps/juce/modules/juce_dsp/maths/juce_LookupTable.h
vendored
Normal file
330
deps/juce/modules/juce_dsp/maths/juce_LookupTable.h
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Class for efficiently approximating expensive arithmetic operations.
|
||||
|
||||
The approximation is based on linear interpolation between pre-calculated values.
|
||||
The approximated function should be passed as a callable object to the constructor
|
||||
along with the number of data points to be pre-calculated. The accuracy of the
|
||||
approximation can be increased by using more points at the cost of a larger memory
|
||||
footprint.
|
||||
|
||||
Consider using LookupTableTransform as an easy-to-use alternative.
|
||||
|
||||
Example:
|
||||
|
||||
LookupTable<float> lut ([] (size_t i) { return std::sqrt ((float) i); }, 64);
|
||||
auto outValue = lut[17];
|
||||
|
||||
@see LookupTableTransform
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class LookupTable
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised LookupTable object.
|
||||
|
||||
You need to call initialise() before using the object. Prefer using the
|
||||
non-default constructor instead.
|
||||
|
||||
@see initialise
|
||||
*/
|
||||
LookupTable();
|
||||
|
||||
/** Creates and initialises a LookupTable object.
|
||||
|
||||
@param functionToApproximate The function to be approximated. This should be a
|
||||
mapping from the integer range [0, numPointsToUse - 1].
|
||||
@param numPointsToUse The number of pre-calculated values stored.
|
||||
*/
|
||||
LookupTable (const std::function<FloatType (size_t)>& functionToApproximate, size_t numPointsToUse);
|
||||
|
||||
/** Initialises or changes the parameters of a LookupTable object.
|
||||
|
||||
This function can be used to change what function is approximated by an already
|
||||
constructed LookupTable along with the number of data points used. If the function
|
||||
to be approximated won't ever change, prefer using the non-default constructor.
|
||||
|
||||
@param functionToApproximate The function to be approximated. This should be a
|
||||
mapping from the integer range [0, numPointsToUse - 1].
|
||||
@param numPointsToUse The number of pre-calculated values stored.
|
||||
*/
|
||||
void initialise (const std::function<FloatType (size_t)>& functionToApproximate, size_t numPointsToUse);
|
||||
|
||||
//==============================================================================
|
||||
/** Calculates the approximated value for the given index without range checking.
|
||||
|
||||
Use this if you can guarantee that the index is non-negative and less than numPoints.
|
||||
Otherwise use get().
|
||||
|
||||
@param index The approximation is calculated for this non-integer index.
|
||||
@return The approximated value at the given index.
|
||||
|
||||
@see get, operator[]
|
||||
*/
|
||||
FloatType getUnchecked (FloatType index) const noexcept
|
||||
{
|
||||
jassert (isInitialised()); // Use the non-default constructor or call initialise() before first use
|
||||
jassert (isPositiveAndBelow (index, FloatType (getNumPoints())));
|
||||
|
||||
auto i = truncatePositiveToUnsignedInt (index);
|
||||
auto f = index - FloatType (i);
|
||||
jassert (isPositiveAndBelow (f, FloatType (1)));
|
||||
|
||||
auto x0 = data.getUnchecked (static_cast<int> (i));
|
||||
auto x1 = data.getUnchecked (static_cast<int> (i + 1));
|
||||
|
||||
return jmap (f, x0, x1);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Calculates the approximated value for the given index with range checking.
|
||||
|
||||
This can be called with any input indices. If the provided index is out-of-range
|
||||
either the bottom or the top element of the LookupTable is returned.
|
||||
|
||||
If the index is guaranteed to be in range use the faster getUnchecked() instead.
|
||||
|
||||
@param index The approximation is calculated for this non-integer index.
|
||||
@return The approximated value at the given index.
|
||||
|
||||
@see getUnchecked, operator[]
|
||||
*/
|
||||
FloatType get (FloatType index) const noexcept
|
||||
{
|
||||
if (index >= (FloatType) getNumPoints())
|
||||
index = static_cast<FloatType> (getGuardIndex());
|
||||
else if (index < 0)
|
||||
index = {};
|
||||
|
||||
return getUnchecked (index);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** @see getUnchecked */
|
||||
FloatType operator[] (FloatType index) const noexcept { return getUnchecked (index); }
|
||||
|
||||
/** Returns the size of the LookupTable, i.e., the number of pre-calculated data points. */
|
||||
size_t getNumPoints() const noexcept { return static_cast<size_t> (data.size()) - 1; }
|
||||
|
||||
/** Returns true if the LookupTable is initialised and ready to be used. */
|
||||
bool isInitialised() const noexcept { return data.size() > 1; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<FloatType> data;
|
||||
|
||||
void prepare() noexcept;
|
||||
static size_t getRequiredBufferSize (size_t numPointsToUse) noexcept { return numPointsToUse + 1; }
|
||||
size_t getGuardIndex() const noexcept { return getRequiredBufferSize (getNumPoints()) - 1; }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookupTable)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Class for approximating expensive arithmetic operations.
|
||||
|
||||
Once initialised, this class can be used just like the function it approximates
|
||||
via operator().
|
||||
|
||||
Example:
|
||||
|
||||
LookupTableTransform<float> tanhApprox ([] (float x) { return std::tanh (x); }, -5.0f, 5.0f, 64);
|
||||
auto outValue = tanhApprox (4.2f);
|
||||
|
||||
Note: If you try to call the function with an input outside the provided
|
||||
range, it will return either the first or the last recorded LookupTable value.
|
||||
|
||||
@see LookupTable
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class LookupTableTransform
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised LookupTableTransform object.
|
||||
|
||||
You need to call initialise() before using the object. Prefer using the
|
||||
non-default constructor instead.
|
||||
|
||||
@see initialise
|
||||
*/
|
||||
LookupTableTransform() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates and initialises a LookupTableTransform object.
|
||||
|
||||
@param functionToApproximate The function to be approximated. This should be a
|
||||
mapping from a FloatType to FloatType.
|
||||
@param minInputValueToUse The lowest input value used. The approximation will
|
||||
fail for values lower than this.
|
||||
@param maxInputValueToUse The highest input value used. The approximation will
|
||||
fail for values higher than this.
|
||||
@param numPoints The number of pre-calculated values stored.
|
||||
*/
|
||||
LookupTableTransform (const std::function<FloatType (FloatType)>& functionToApproximate,
|
||||
FloatType minInputValueToUse,
|
||||
FloatType maxInputValueToUse,
|
||||
size_t numPoints)
|
||||
{
|
||||
initialise (functionToApproximate, minInputValueToUse, maxInputValueToUse, numPoints);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises or changes the parameters of a LookupTableTransform object.
|
||||
|
||||
@param functionToApproximate The function to be approximated. This should be a
|
||||
mapping from a FloatType to FloatType.
|
||||
@param minInputValueToUse The lowest input value used. The approximation will
|
||||
fail for values lower than this.
|
||||
@param maxInputValueToUse The highest input value used. The approximation will
|
||||
fail for values higher than this.
|
||||
@param numPoints The number of pre-calculated values stored.
|
||||
*/
|
||||
void initialise (const std::function<FloatType (FloatType)>& functionToApproximate,
|
||||
FloatType minInputValueToUse,
|
||||
FloatType maxInputValueToUse,
|
||||
size_t numPoints);
|
||||
|
||||
//==============================================================================
|
||||
/** Calculates the approximated value for the given input value without range checking.
|
||||
|
||||
Use this if you can guarantee that the input value is within the range specified
|
||||
in the constructor or initialise(), otherwise use processSample().
|
||||
|
||||
@param value The approximation is calculated for this input value.
|
||||
@return The approximated value for the provided input value.
|
||||
|
||||
@see processSample, operator(), operator[]
|
||||
*/
|
||||
FloatType processSampleUnchecked (FloatType value) const noexcept
|
||||
{
|
||||
jassert (value >= minInputValue && value <= maxInputValue);
|
||||
return lookupTable[scaler * value + offset];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Calculates the approximated value for the given input value with range checking.
|
||||
|
||||
This can be called with any input values. Out-of-range input values will be
|
||||
clipped to the specified input range.
|
||||
|
||||
If the index is guaranteed to be in range use the faster processSampleUnchecked()
|
||||
instead.
|
||||
|
||||
@param value The approximation is calculated for this input value.
|
||||
@return The approximated value for the provided input value.
|
||||
|
||||
@see processSampleUnchecked, operator(), operator[]
|
||||
*/
|
||||
FloatType processSample (FloatType value) const noexcept
|
||||
{
|
||||
auto index = scaler * jlimit (minInputValue, maxInputValue, value) + offset;
|
||||
jassert (isPositiveAndBelow (index, FloatType (lookupTable.getNumPoints())));
|
||||
|
||||
return lookupTable[index];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** @see processSampleUnchecked */
|
||||
FloatType operator[] (FloatType index) const noexcept { return processSampleUnchecked (index); }
|
||||
|
||||
/** @see processSample */
|
||||
FloatType operator() (FloatType index) const noexcept { return processSample (index); }
|
||||
|
||||
//==============================================================================
|
||||
/** Processes an array of input values without range checking
|
||||
@see process
|
||||
*/
|
||||
void processUnchecked (const FloatType* input, FloatType* output, size_t numSamples) const noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
output[i] = processSampleUnchecked (input[i]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Processes an array of input values with range checking
|
||||
@see processUnchecked
|
||||
*/
|
||||
void process (const FloatType* input, FloatType* output, size_t numSamples) const noexcept
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
output[i] = processSample (input[i]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Calculates the maximum relative error of the approximation for the specified
|
||||
parameter set.
|
||||
|
||||
The closer the returned value is to zero the more accurate the approximation
|
||||
is.
|
||||
|
||||
This function compares the approximated output of this class to the function
|
||||
it approximates at a range of points and returns the maximum relative error.
|
||||
This can be used to determine if the approximation is suitable for the given
|
||||
problem. The accuracy of the approximation can generally be improved by
|
||||
increasing numPoints.
|
||||
|
||||
@param functionToApproximate The approximated function. This should be a
|
||||
mapping from a FloatType to FloatType.
|
||||
@param minInputValue The lowest input value used.
|
||||
@param maxInputValue The highest input value used.
|
||||
@param numPoints The number of pre-calculated values stored.
|
||||
@param numTestPoints The number of input values used for error
|
||||
calculation. Higher numbers can increase the
|
||||
accuracy of the error calculation. If it's zero
|
||||
then 100 * numPoints will be used.
|
||||
*/
|
||||
static double calculateMaxRelativeError (const std::function<FloatType (FloatType)>& functionToApproximate,
|
||||
FloatType minInputValue,
|
||||
FloatType maxInputValue,
|
||||
size_t numPoints,
|
||||
size_t numTestPoints = 0);
|
||||
private:
|
||||
//==============================================================================
|
||||
static double calculateRelativeDifference (double, double) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
LookupTable<FloatType> lookupTable;
|
||||
|
||||
FloatType minInputValue, maxInputValue;
|
||||
FloatType scaler, offset;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookupTableTransform)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
317
deps/juce/modules/juce_dsp/maths/juce_Matrix.cpp
vendored
Normal file
317
deps/juce/modules/juce_dsp/maths/juce_Matrix.cpp
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 ElementType>
|
||||
Matrix<ElementType> Matrix<ElementType>::identity (size_t size)
|
||||
{
|
||||
Matrix result (size, size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result(i, i) = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ElementType>
|
||||
Matrix<ElementType> Matrix<ElementType>::toeplitz (const Matrix& vector, size_t size)
|
||||
{
|
||||
jassert (vector.isOneColumnVector());
|
||||
jassert (size <= vector.rows);
|
||||
|
||||
Matrix result (size, size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result (i, i) = vector (0, 0);
|
||||
|
||||
for (size_t i = 1; i < size; ++i)
|
||||
for (size_t j = i; j < size; ++j)
|
||||
result (j, j - i) = result (j - i, j) = vector (i, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ElementType>
|
||||
Matrix<ElementType> Matrix<ElementType>::hankel (const Matrix& vector, size_t size, size_t offset)
|
||||
{
|
||||
jassert(vector.isOneColumnVector());
|
||||
jassert(vector.rows >= (2 * (size - 1) + 1));
|
||||
|
||||
Matrix result (size, size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result (i, i) = vector ((2 * i) + offset, 0);
|
||||
|
||||
for (size_t i = 1; i < size; ++i)
|
||||
for (size_t j = i; j < size; ++j)
|
||||
result (j, j - i) = result (j - i, j) = vector (i + 2 * (j - i) + offset, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename ElementType>
|
||||
Matrix<ElementType>& Matrix<ElementType>::swapColumns (size_t columnOne, size_t columnTwo) noexcept
|
||||
{
|
||||
jassert (columnOne < columns && columnTwo < columns);
|
||||
|
||||
auto* p = data.getRawDataPointer();
|
||||
|
||||
for (size_t i = 0; i < rows; ++i)
|
||||
{
|
||||
auto offset = dataAcceleration.getUnchecked (static_cast<int> (i));
|
||||
std::swap (p[offset + columnOne], p[offset + columnTwo]);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename ElementType>
|
||||
Matrix<ElementType>& Matrix<ElementType>::swapRows (size_t rowOne, size_t rowTwo) noexcept
|
||||
{
|
||||
jassert (rowOne < rows && rowTwo < rows);
|
||||
|
||||
auto offset1 = rowOne * columns;
|
||||
auto offset2 = rowTwo * columns;
|
||||
|
||||
auto* p = data.getRawDataPointer();
|
||||
|
||||
for (size_t i = 0; i < columns; ++i)
|
||||
std::swap (p[offset1 + i], p[offset2 + i]);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename ElementType>
|
||||
Matrix<ElementType> Matrix<ElementType>::operator* (const Matrix<ElementType>& other) const
|
||||
{
|
||||
auto n = getNumRows(), m = other.getNumColumns(), p = getNumColumns();
|
||||
Matrix result (n, m);
|
||||
|
||||
jassert (p == other.getNumRows());
|
||||
|
||||
size_t offsetMat = 0, offsetlhs = 0;
|
||||
|
||||
auto* dst = result.getRawDataPointer();
|
||||
auto* a = getRawDataPointer();
|
||||
auto* b = other.getRawDataPointer();
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
size_t offsetrhs = 0;
|
||||
|
||||
for (size_t k = 0; k < p; ++k)
|
||||
{
|
||||
auto ak = a[offsetlhs++];
|
||||
|
||||
for (size_t j = 0; j < m; ++j)
|
||||
dst[offsetMat + j] += ak * b[offsetrhs + j];
|
||||
|
||||
offsetrhs += m;
|
||||
}
|
||||
|
||||
offsetMat += m;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename ElementType>
|
||||
bool Matrix<ElementType>::compare (const Matrix& a, const Matrix& b, ElementType tolerance) noexcept
|
||||
{
|
||||
if (a.rows != b.rows || a.columns != b.columns)
|
||||
return false;
|
||||
|
||||
tolerance = std::abs (tolerance);
|
||||
|
||||
auto* bPtr = b.begin();
|
||||
for (auto aValue : a)
|
||||
if (std::abs (aValue - *bPtr++) > tolerance)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename ElementType>
|
||||
bool Matrix<ElementType>::solve (Matrix& b) const noexcept
|
||||
{
|
||||
auto n = columns;
|
||||
jassert (n == n && n == b.rows && b.isOneColumnVector());
|
||||
|
||||
auto* x = b.getRawDataPointer();
|
||||
const auto& A = *this;
|
||||
|
||||
switch (n)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
auto denominator = A (0,0);
|
||||
|
||||
if (denominator == 0)
|
||||
return false;
|
||||
|
||||
b (0, 0) /= denominator;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
auto denominator = A (0, 0) * A (1, 1) - A (0, 1) * A (1, 0);
|
||||
|
||||
if (denominator == 0)
|
||||
return false;
|
||||
|
||||
auto factor = (1 / denominator);
|
||||
auto b0 = x[0], b1 = x[1];
|
||||
|
||||
x[0] = factor * (A (1, 1) * b0 - A (0, 1) * b1);
|
||||
x[1] = factor * (A (0, 0) * b1 - A (1, 0) * b0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
auto denominator = A (0, 0) * (A (1, 1) * A (2, 2) - A (1, 2) * A (2, 1))
|
||||
+ A (0, 1) * (A (1, 2) * A (2, 0) - A (1, 0) * A (2, 2))
|
||||
+ A (0, 2) * (A (1, 0) * A (2, 1) - A (1, 1) * A (2, 0));
|
||||
|
||||
if (denominator == 0)
|
||||
return false;
|
||||
|
||||
auto factor = 1 / denominator;
|
||||
auto b0 = x[0], b1 = x[1], b2 = x[2];
|
||||
|
||||
x[0] = ( ( A (0, 1) * A (1, 2) - A (0, 2) * A (1, 1)) * b2
|
||||
+ (-A (0, 1) * A (2, 2) + A (0, 2) * A (2, 1)) * b1
|
||||
+ ( A (1, 1) * A (2, 2) - A (1, 2) * A (2, 1)) * b0) * factor;
|
||||
|
||||
x[1] = -( ( A (0, 0) * A (1, 2) - A (0, 2) * A (1, 0)) * b2
|
||||
+ (-A (0, 0) * A (2, 2) + A (0, 2) * A (2, 0)) * b1
|
||||
+ ( A (1, 0) * A (2, 2) - A (1, 2) * A (2, 0)) * b0) * factor;
|
||||
|
||||
x[2] = ( ( A (0, 0) * A (1, 1) - A (0, 1) * A (1, 0)) * b2
|
||||
+ (-A (0, 0) * A (2, 1) + A (0, 1) * A (2, 0)) * b1
|
||||
+ ( A (1, 0) * A (2, 1) - A (1, 1) * A (2, 0)) * b0) * factor;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
Matrix<ElementType> M (A);
|
||||
|
||||
for (size_t j = 0; j < n; ++j)
|
||||
{
|
||||
if (M (j, j) == 0)
|
||||
{
|
||||
auto i = j;
|
||||
while (i < n && M (i, j) == 0)
|
||||
++i;
|
||||
|
||||
if (i == n)
|
||||
return false;
|
||||
|
||||
for (size_t k = 0; k < n; ++k)
|
||||
M (j, k) += M (i, k);
|
||||
|
||||
x[j] += x[i];
|
||||
}
|
||||
|
||||
auto t = 1 / M (j, j);
|
||||
|
||||
for (size_t k = 0; k < n; ++k)
|
||||
M (j, k) *= t;
|
||||
|
||||
x[j] *= t;
|
||||
|
||||
for (size_t k = j + 1; k < n; ++k)
|
||||
{
|
||||
auto u = -M (k, j);
|
||||
|
||||
for (size_t l = 0; l < n; ++l)
|
||||
M (k, l) += u * M (j, l);
|
||||
|
||||
x[k] += u * x[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = static_cast<int> (n) - 2; k >= 0; --k)
|
||||
for (size_t i = static_cast<size_t> (k) + 1; i < n; ++i)
|
||||
x[k] -= M (static_cast<size_t> (k), i) * x[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename ElementType>
|
||||
String Matrix<ElementType>::toString() const
|
||||
{
|
||||
StringArray entries;
|
||||
int sizeMax = 0;
|
||||
|
||||
auto* p = data.begin();
|
||||
|
||||
for (size_t i = 0; i < rows; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < columns; ++j)
|
||||
{
|
||||
String entry (*p++, 4);
|
||||
sizeMax = jmax (sizeMax, entry.length());
|
||||
|
||||
entries.add (entry);
|
||||
}
|
||||
}
|
||||
|
||||
sizeMax = ((sizeMax + 1) / 4 + 1) * 4;
|
||||
|
||||
MemoryOutputStream result;
|
||||
|
||||
auto n = static_cast<size_t> (entries.size());
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
result << entries[(int) i].paddedRight (' ', sizeMax);
|
||||
|
||||
if (i % columns == (columns - 1))
|
||||
result << newLine;
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
template class Matrix<float>;
|
||||
template class Matrix<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
254
deps/juce/modules/juce_dsp/maths/juce_Matrix.h
vendored
Normal file
254
deps/juce/modules/juce_dsp/maths/juce_Matrix.h
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
General matrix and vectors class, meant for classic math manipulation such as
|
||||
additions, multiplications, and linear systems of equations solving.
|
||||
|
||||
@see LinearAlgebra
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename ElementType>
|
||||
class Matrix
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a new matrix with a given number of rows and columns. */
|
||||
Matrix (size_t numRows, size_t numColumns)
|
||||
: rows (numRows), columns (numColumns)
|
||||
{
|
||||
resize();
|
||||
clear();
|
||||
}
|
||||
|
||||
/** Creates a new matrix with a given number of rows and columns, with initial
|
||||
data coming from an array, stored in row-major order.
|
||||
*/
|
||||
Matrix (size_t numRows, size_t numColumns, const ElementType* dataPointer)
|
||||
: rows (numRows), columns (numColumns)
|
||||
{
|
||||
resize();
|
||||
memcpy (data.getRawDataPointer(), dataPointer, rows * columns * sizeof (ElementType));
|
||||
}
|
||||
|
||||
/** Creates a copy of another matrix. */
|
||||
Matrix (const Matrix&) = default;
|
||||
|
||||
/** Moves a copy of another matrix. */
|
||||
Matrix (Matrix&&) noexcept = default;
|
||||
|
||||
/** Creates a copy of another matrix. */
|
||||
Matrix& operator= (const Matrix&) = default;
|
||||
|
||||
/** Moves another matrix into this one */
|
||||
Matrix& operator= (Matrix&&) noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates the identity matrix */
|
||||
static Matrix identity (size_t size);
|
||||
|
||||
/** Creates a Toeplitz Matrix from a vector with a given squared size */
|
||||
static Matrix toeplitz (const Matrix& vector, size_t size);
|
||||
|
||||
/** Creates a squared size x size Hankel Matrix from a vector with an optional offset.
|
||||
|
||||
@param vector The vector from which the Hankel matrix should be generated.
|
||||
Its number of rows should be at least 2 * (size - 1) + 1
|
||||
@param size The size of resulting square matrix.
|
||||
@param offset An optional offset into the given vector.
|
||||
*/
|
||||
static Matrix hankel (const Matrix& vector, size_t size, size_t offset = 0);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of rows in the matrix. */
|
||||
size_t getNumRows() const noexcept { return rows; }
|
||||
|
||||
/** Returns the number of columns in the matrix. */
|
||||
size_t getNumColumns() const noexcept { return columns; }
|
||||
|
||||
/** Returns an Array of 2 integers with the number of rows and columns in the
|
||||
matrix.
|
||||
*/
|
||||
Array<size_t> getSize() const noexcept { return { rows, columns }; }
|
||||
|
||||
/** Fills the contents of the matrix with zeroes. */
|
||||
void clear() noexcept { zeromem (data.begin(), (size_t) data.size() * sizeof (ElementType)); }
|
||||
|
||||
//==============================================================================
|
||||
/** Swaps the contents of two rows in the matrix and returns a reference to itself. */
|
||||
Matrix& swapRows (size_t rowOne, size_t rowTwo) noexcept;
|
||||
|
||||
/** Swaps the contents of two columns in the matrix and returns a reference to itself. */
|
||||
Matrix& swapColumns (size_t columnOne, size_t columnTwo) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the value of the matrix at a given row and column (for reading). */
|
||||
inline ElementType operator() (size_t row, size_t column) const noexcept
|
||||
{
|
||||
jassert (row < rows && column < columns);
|
||||
return data.getReference (static_cast<int> (dataAcceleration.getReference (static_cast<int> (row))) + static_cast<int> (column));
|
||||
}
|
||||
|
||||
/** Returns the value of the matrix at a given row and column (for modifying). */
|
||||
inline ElementType& operator() (size_t row, size_t column) noexcept
|
||||
{
|
||||
jassert (row < rows && column < columns);
|
||||
return data.getReference (static_cast<int> (dataAcceleration.getReference (static_cast<int> (row))) + static_cast<int> (column));
|
||||
}
|
||||
|
||||
/** Returns a pointer to the raw data of the matrix object, ordered in row-major
|
||||
order (for modifying).
|
||||
*/
|
||||
inline ElementType* getRawDataPointer() noexcept { return data.getRawDataPointer(); }
|
||||
|
||||
/** Returns a pointer to the raw data of the matrix object, ordered in row-major
|
||||
order (for reading).
|
||||
*/
|
||||
inline const ElementType* getRawDataPointer() const noexcept { return data.begin(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Addition of two matrices */
|
||||
inline Matrix& operator+= (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a + b; } ); }
|
||||
|
||||
/** Subtraction of two matrices */
|
||||
inline Matrix& operator-= (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a - b; } ); }
|
||||
|
||||
/** Scalar multiplication */
|
||||
inline Matrix& operator*= (ElementType scalar) noexcept
|
||||
{
|
||||
std::for_each (begin(), end(), [scalar] (ElementType& x) { x *= scalar; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Addition of two matrices */
|
||||
inline Matrix operator+ (const Matrix& other) const { Matrix result (*this); result += other; return result; }
|
||||
|
||||
/** Addition of two matrices */
|
||||
inline Matrix operator- (const Matrix& other) const { Matrix result (*this); result -= other; return result; }
|
||||
|
||||
/** Scalar multiplication */
|
||||
inline Matrix operator* (ElementType scalar) const { Matrix result (*this); result *= scalar; return result; }
|
||||
|
||||
/** Matrix multiplication */
|
||||
Matrix operator* (const Matrix& other) const;
|
||||
|
||||
/** Does a hadarmard product with the receiver and other and stores the result in the receiver */
|
||||
inline Matrix& hadarmard (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a * b; } ); }
|
||||
|
||||
/** Does a hadarmard product with a and b returns the result. */
|
||||
static Matrix hadarmard (const Matrix& a, const Matrix& b) { Matrix result (a); result.hadarmard (b); return result; }
|
||||
|
||||
//==============================================================================
|
||||
/** Compare to matrices with a given tolerance */
|
||||
static bool compare (const Matrix& a, const Matrix& b, ElementType tolerance = 0) noexcept;
|
||||
|
||||
/* Comparison operator */
|
||||
inline bool operator== (const Matrix& other) const noexcept { return compare (*this, other); }
|
||||
|
||||
//==============================================================================
|
||||
/** Tells if the matrix is a square matrix */
|
||||
bool isSquare() const noexcept { return rows == columns; }
|
||||
|
||||
/** Tells if the matrix is a vector */
|
||||
bool isVector() const noexcept { return isOneColumnVector() || isOneRowVector(); }
|
||||
|
||||
/** Tells if the matrix is a one column vector */
|
||||
bool isOneColumnVector() const noexcept { return columns == 1; }
|
||||
|
||||
/** Tells if the matrix is a one row vector */
|
||||
bool isOneRowVector() const noexcept { return rows == 1; }
|
||||
|
||||
/** Tells if the matrix is a null matrix */
|
||||
bool isNullMatrix() const noexcept { return rows == 0 || columns == 0; }
|
||||
|
||||
//==============================================================================
|
||||
/** Solves a linear system of equations represented by this object and the argument b,
|
||||
using various algorithms depending on the size of the arguments.
|
||||
|
||||
The matrix must be a square matrix N times N, and b must be a vector N times 1,
|
||||
with the coefficients of b. After the execution of the algorithm,
|
||||
the vector b will contain the solution.
|
||||
|
||||
Returns true if the linear system of equations was successfully solved.
|
||||
*/
|
||||
bool solve (Matrix& b) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a String displaying in a convenient way the matrix contents. */
|
||||
String toString() const;
|
||||
|
||||
//==============================================================================
|
||||
ElementType* begin() noexcept { return data.begin(); }
|
||||
ElementType* end() noexcept { return data.end(); }
|
||||
|
||||
const ElementType* begin() const noexcept { return &data.getReference (0); }
|
||||
const ElementType* end() const noexcept { return begin() + data.size(); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
/** Resizes the matrix. */
|
||||
void resize()
|
||||
{
|
||||
data.resize (static_cast<int> (columns * rows));
|
||||
dataAcceleration.resize (static_cast<int> (rows));
|
||||
|
||||
for (size_t i = 0; i < rows; ++i)
|
||||
dataAcceleration.setUnchecked (static_cast<int> (i), i * columns);
|
||||
}
|
||||
|
||||
template <typename BinaryOperation>
|
||||
Matrix& apply (const Matrix& other, BinaryOperation binaryOp)
|
||||
{
|
||||
jassert (rows == other.rows && columns == other.columns);
|
||||
|
||||
auto* dst = getRawDataPointer();
|
||||
|
||||
for (auto src : other)
|
||||
{
|
||||
*dst = binaryOp (*dst, src);
|
||||
++dst;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<ElementType> data;
|
||||
Array<size_t> dataAcceleration;
|
||||
|
||||
size_t rows, columns;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (Matrix)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
173
deps/juce/modules/juce_dsp/maths/juce_Matrix_test.cpp
vendored
Normal file
173
deps/juce/modules/juce_dsp/maths/juce_Matrix_test.cpp
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
struct LinearAlgebraUnitTest : public UnitTest
|
||||
{
|
||||
LinearAlgebraUnitTest()
|
||||
: UnitTest ("Linear Algebra UnitTests", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
struct AdditionTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const ElementType data2[] = { 1, -1, 3, -1, 5, -1, 7, -1 };
|
||||
const ElementType data3[] = { 2, 1, 6, 3, 10, 5, 14, 7 };
|
||||
|
||||
Matrix<ElementType> mat1 (2, 4, data1);
|
||||
Matrix<ElementType> mat2 (2, 4, data2);
|
||||
Matrix<ElementType> mat3 (2, 4, data3);
|
||||
|
||||
u.expect((mat1 + mat2) == mat3);
|
||||
}
|
||||
};
|
||||
|
||||
struct DifferenceTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const ElementType data2[] = { 1, -1, 3, -1, 5, -1, 7, -1 };
|
||||
const ElementType data3[] = { 0, 3, 0, 5, 0, 7, 0, 9 };
|
||||
|
||||
Matrix<ElementType> mat1 (2, 4, data1);
|
||||
Matrix<ElementType> mat2 (2, 4, data2);
|
||||
Matrix<ElementType> mat3 (2, 4, data3);
|
||||
|
||||
u.expect((mat1 - mat2) == mat3);
|
||||
}
|
||||
};
|
||||
|
||||
struct ScalarMultiplicationTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const ElementType scalar = 2.0;
|
||||
const ElementType data2[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
|
||||
|
||||
Matrix<ElementType> x (2, 4, data1);
|
||||
Matrix<ElementType> expected (2, 4, data2);
|
||||
|
||||
u.expect ((x * scalar) == expected);
|
||||
}
|
||||
};
|
||||
|
||||
struct HadamardProductTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const ElementType data2[] = { 1, -1, 3, -1, 5, -1, 7, -1 };
|
||||
const ElementType data3[] = { 1, -2, 9, -4, 25, -6, 49, -8 };
|
||||
|
||||
Matrix<ElementType> mat1 (2, 4, data1);
|
||||
Matrix<ElementType> mat2 (2, 4, data2);
|
||||
Matrix<ElementType> mat3 (2, 4, data3);
|
||||
|
||||
u.expect (Matrix<ElementType>::hadarmard (mat1, mat2) == mat3);
|
||||
}
|
||||
};
|
||||
|
||||
struct MultiplicationTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const ElementType data2[] = { 1, -1, 3, -1, 5, -1, 7, -1 };
|
||||
const ElementType data3[] = { 50, -10, 114, -26 };
|
||||
|
||||
Matrix<ElementType> mat1 (2, 4, data1);
|
||||
Matrix<ElementType> mat2 (4, 2, data2);
|
||||
Matrix<ElementType> mat3 (2, 2, data3);
|
||||
|
||||
u.expect((mat1 * mat2) == mat3);
|
||||
}
|
||||
};
|
||||
|
||||
struct IdentityMatrixTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
|
||||
u.expect (Matrix<ElementType>::identity (4) == Matrix<ElementType> (4, 4, data1));
|
||||
}
|
||||
};
|
||||
|
||||
struct SolvingTest
|
||||
{
|
||||
template <typename ElementType>
|
||||
static void run (LinearAlgebraUnitTest& u)
|
||||
{
|
||||
const ElementType data1[] = { 1, -1, 2, -2 };
|
||||
const ElementType data2[] = { -1, 0, -1, -7 };
|
||||
const ElementType data3[] = { 1, 4, 2, 1, -1, 1, 4, 3, -2, -1, 1, 1, -1, 0, 1, 4 };
|
||||
|
||||
Matrix<ElementType> X (4, 1, data1);
|
||||
Matrix<ElementType> B (4, 1, data2);
|
||||
Matrix<ElementType> A (4, 4, data3);
|
||||
|
||||
u.expect (A.solve (B));
|
||||
u.expect (Matrix<ElementType>::compare (X, B, (ElementType) 1e-4));
|
||||
}
|
||||
};
|
||||
|
||||
template <class TheTest>
|
||||
void runTestForAllTypes (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
TheTest::template run<float> (*this);
|
||||
TheTest::template run<double> (*this);
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
runTestForAllTypes<AdditionTest> ("AdditionTest");
|
||||
runTestForAllTypes<DifferenceTest> ("DifferenceTest");
|
||||
runTestForAllTypes<ScalarMultiplicationTest> ("ScalarMultiplication");
|
||||
runTestForAllTypes<HadamardProductTest> ("HadamardProductTest");
|
||||
runTestForAllTypes<MultiplicationTest> ("MultiplicationTest");
|
||||
runTestForAllTypes<IdentityMatrixTest> ("IdentityMatrixTest");
|
||||
runTestForAllTypes<SolvingTest> ("SolvingTest");
|
||||
}
|
||||
};
|
||||
|
||||
static LinearAlgebraUnitTest linearAlgebraUnitTest;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
67
deps/juce/modules/juce_dsp/maths/juce_Phase.h
vendored
Normal file
67
deps/juce/modules/juce_dsp/maths/juce_Phase.h
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Represents an increasing phase value between 0 and 2*pi.
|
||||
|
||||
This represents a value which can be incremented, and which wraps back to 0 when it
|
||||
goes past 2 * pi.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct Phase
|
||||
{
|
||||
/** Resets the phase to 0. */
|
||||
void reset() noexcept { phase = 0; }
|
||||
|
||||
/** Returns the current value, and increments the phase by the given increment.
|
||||
The increment must be a positive value, it can't go backwards!
|
||||
The new value of the phase after calling this function will be (phase + increment) % (2 * pi).
|
||||
*/
|
||||
Type advance (Type increment) noexcept
|
||||
{
|
||||
jassert (increment >= 0); // cannot run this value backwards!
|
||||
|
||||
auto last = phase;
|
||||
auto next = last + increment;
|
||||
|
||||
while (next >= MathConstants<Type>::twoPi)
|
||||
next -= MathConstants<Type>::twoPi;
|
||||
|
||||
phase = next;
|
||||
return last;
|
||||
}
|
||||
|
||||
Type phase = 0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
168
deps/juce/modules/juce_dsp/maths/juce_Polynomial.h
vendored
Normal file
168
deps/juce/modules/juce_dsp/maths/juce_Polynomial.h
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A class representing a polynomial
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatingType>
|
||||
class Polynomial
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a new polynomial which will always evaluate to zero. */
|
||||
Polynomial()
|
||||
{
|
||||
coeffs.add (0);
|
||||
}
|
||||
|
||||
/** Creates a new polynomial with given coefficients.
|
||||
|
||||
@param numCoefficients The number of coefficients stored in coefficients.
|
||||
This is also the order of the returned polynomial.
|
||||
@param coefficients The coefficients which will be used by the newly
|
||||
created polynomial. The Polynomial class will keep
|
||||
a private copy of the coefficients.
|
||||
*/
|
||||
Polynomial (const FloatingType* coefficients, int numCoefficients)
|
||||
: coeffs (coefficients, numCoefficients)
|
||||
{
|
||||
jassert (! coeffs.isEmpty());
|
||||
}
|
||||
|
||||
/** Creates a copy of another polynomial. */
|
||||
Polynomial (const Polynomial&) = default;
|
||||
|
||||
/** Creates a copy of another polynomial. */
|
||||
Polynomial (Polynomial&&) = default;
|
||||
|
||||
/** Creates a copy of another polynomial. */
|
||||
Polynomial& operator= (const Polynomial&) = default;
|
||||
|
||||
/** Creates a copy of another polynomial. */
|
||||
Polynomial& operator= (Polynomial&&) = default;
|
||||
|
||||
/** Creates a new polynomial with coefficients by a C++11 initializer list.
|
||||
This function can be used in the following way:
|
||||
Polynomial<float> p ({0.5f, -0.3f, 0.2f});
|
||||
*/
|
||||
template <typename... Values>
|
||||
Polynomial (Values... items) : coeffs (items...)
|
||||
{
|
||||
jassert (! coeffs.isEmpty());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a single coefficient of the receiver for reading */
|
||||
FloatingType operator[] (int index) const noexcept { return coeffs.getUnchecked (index); }
|
||||
|
||||
/** Returns a single coefficient of the receiver for modifying. */
|
||||
FloatingType& operator[] (int index) noexcept { return coeffs.getReference (index); }
|
||||
|
||||
/** Evaluates the value of the polynomial at a single point x. */
|
||||
FloatingType operator() (FloatingType x) const noexcept
|
||||
{
|
||||
// Horner's method
|
||||
FloatingType y (0);
|
||||
|
||||
for (int i = coeffs.size(); --i >= 0;)
|
||||
y = (x * y) + coeffs.getUnchecked(i);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
/** Returns the order of the polynomial. */
|
||||
int getOrder() noexcept
|
||||
{
|
||||
return coeffs.size() - 1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the polynomial with all its coefficients multiplied with a gain factor */
|
||||
Polynomial<FloatingType> withGain (double gain) const
|
||||
{
|
||||
auto result = *this;
|
||||
|
||||
for (auto& c : result.coeffs)
|
||||
c *= gain;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns the sum of this polynomial with another */
|
||||
Polynomial<FloatingType> getSumWith (const Polynomial<FloatingType>& other) const
|
||||
{
|
||||
if (coeffs.size() < other.coeffs.size())
|
||||
return other.getSumWith (*this);
|
||||
|
||||
auto result = *this;
|
||||
|
||||
for (int i = 0; i < other.coeffs.size(); ++i)
|
||||
result[i] += other[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** computes the product of two polynomials and return the result */
|
||||
Polynomial<FloatingType> getProductWith (const Polynomial<FloatingType>& other) const
|
||||
{
|
||||
Polynomial<FloatingType> result;
|
||||
result.coeffs.clearQuick();
|
||||
|
||||
auto N1 = coeffs.size();
|
||||
auto N2 = other.coeffs.size();
|
||||
auto Nmax = jmax (N1, N2);
|
||||
|
||||
auto N = N1 + N2 - 1;
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
FloatingType value (0);
|
||||
|
||||
for (int j = 0; j < Nmax; ++j)
|
||||
if (j >= 0 && j < N1 && i - j >= 0 && i - j < N2)
|
||||
value = value + (*this)[j] * other[i - j];
|
||||
|
||||
result.coeffs.add (value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<FloatingType> coeffs;
|
||||
|
||||
JUCE_LEAK_DETECTOR (Polynomial)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
143
deps/juce/modules/juce_dsp/maths/juce_SpecialFunctions.cpp
vendored
Normal file
143
deps/juce/modules/juce_dsp/maths/juce_SpecialFunctions.cpp
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
double SpecialFunctions::besselI0 (double x) noexcept
|
||||
{
|
||||
auto ax = std::abs (x);
|
||||
|
||||
if (ax < 3.75)
|
||||
{
|
||||
auto y = x / 3.75;
|
||||
y *= y;
|
||||
|
||||
return 1.0 + y * (3.5156229 + y * (3.0899424 + y * (1.2067492
|
||||
+ y * (0.2659732 + y * (0.360768e-1 + y * 0.45813e-2)))));
|
||||
}
|
||||
|
||||
auto y = 3.75 / ax;
|
||||
|
||||
return (std::exp (ax) / std::sqrt (ax))
|
||||
* (0.39894228 + y * (0.1328592e-1 + y * (0.225319e-2 + y * (-0.157565e-2 + y * (0.916281e-2
|
||||
+ y * (-0.2057706e-1 + y * (0.2635537e-1 + y * (-0.1647633e-1 + y * 0.392377e-2))))))));
|
||||
}
|
||||
|
||||
void SpecialFunctions::ellipticIntegralK (double k, double& K, double& Kp) noexcept
|
||||
{
|
||||
constexpr int M = 4;
|
||||
|
||||
K = MathConstants<double>::halfPi;
|
||||
auto lastK = k;
|
||||
|
||||
for (int i = 0; i < M; ++i)
|
||||
{
|
||||
lastK = std::pow (lastK / (1 + std::sqrt (1 - std::pow (lastK, 2.0))), 2.0);
|
||||
K *= 1 + lastK;
|
||||
}
|
||||
|
||||
Kp = MathConstants<double>::halfPi;
|
||||
auto last = std::sqrt (1 - k * k);
|
||||
|
||||
for (int i = 0; i < M; ++i)
|
||||
{
|
||||
last = std::pow (last / (1.0 + std::sqrt (1.0 - std::pow (last, 2.0))), 2.0);
|
||||
Kp *= 1 + last;
|
||||
}
|
||||
}
|
||||
|
||||
Complex<double> SpecialFunctions::cde (Complex<double> u, double k) noexcept
|
||||
{
|
||||
constexpr int M = 4;
|
||||
|
||||
double ke[M + 1];
|
||||
double* kei = ke;
|
||||
*kei = k;
|
||||
|
||||
for (int i = 0; i < M; ++i)
|
||||
{
|
||||
auto next = std::pow (*kei / (1.0 + std::sqrt (1.0 - std::pow (*kei, 2.0))), 2.0);
|
||||
*++kei = next;
|
||||
}
|
||||
|
||||
// NB: the spurious cast to double here is a workaround for a very odd link-time failure
|
||||
std::complex<double> last = std::cos (u * (double) MathConstants<double>::halfPi);
|
||||
|
||||
for (int i = M - 1; i >= 0; --i)
|
||||
last = (1.0 + ke[i + 1]) / (1.0 / last + ke[i + 1] * last);
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
Complex<double> SpecialFunctions::sne (Complex<double> u, double k) noexcept
|
||||
{
|
||||
constexpr int M = 4;
|
||||
|
||||
double ke[M + 1];
|
||||
double* kei = ke;
|
||||
*kei = k;
|
||||
|
||||
for (int i = 0; i < M; ++i)
|
||||
{
|
||||
auto next = std::pow (*kei / (1 + std::sqrt (1 - std::pow (*kei, 2.0))), 2.0);
|
||||
*++kei = next;
|
||||
}
|
||||
|
||||
// NB: the spurious cast to double here is a workaround for a very odd link-time failure
|
||||
std::complex<double> last = std::sin (u * (double) MathConstants<double>::halfPi);
|
||||
|
||||
for (int i = M - 1; i >= 0; --i)
|
||||
last = (1.0 + ke[i + 1]) / (1.0 / last + ke[i + 1] * last);
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
Complex<double> SpecialFunctions::asne (Complex<double> w, double k) noexcept
|
||||
{
|
||||
constexpr int M = 4;
|
||||
|
||||
double ke[M + 1];
|
||||
double* kei = ke;
|
||||
*kei = k;
|
||||
|
||||
for (int i = 0; i < M; ++i)
|
||||
{
|
||||
auto next = std::pow (*kei / (1.0 + std::sqrt (1.0 - std::pow (*kei, 2.0))), 2.0);
|
||||
*++kei = next;
|
||||
}
|
||||
|
||||
std::complex<double> last = w;
|
||||
|
||||
for (int i = 1; i <= M; ++i)
|
||||
last = 2.0 * last / ((1.0 + ke[i]) * (1.0 + std::sqrt (1.0 - std::pow (ke[i - 1] * last, 2.0))));
|
||||
|
||||
return 2.0 / MathConstants<double>::pi * std::asin (last);
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
67
deps/juce/modules/juce_dsp/maths/juce_SpecialFunctions.h
vendored
Normal file
67
deps/juce/modules/juce_dsp/maths/juce_SpecialFunctions.h
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Contains miscellaneous filter design and windowing functions.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct SpecialFunctions
|
||||
{
|
||||
/** Computes the modified Bessel function of the first kind I0 for a
|
||||
given double value x. Modified Bessel functions are useful to solve
|
||||
various mathematical problems involving differential equations.
|
||||
*/
|
||||
static double besselI0 (double x) noexcept;
|
||||
|
||||
/** Computes the complete elliptic integral of the first kind K for a
|
||||
given double value k, and the associated complete elliptic integral
|
||||
of the first kind Kp for the complementary modulus of k.
|
||||
*/
|
||||
static void ellipticIntegralK (double k, double& K, double& Kp) noexcept;
|
||||
|
||||
/** Computes the Jacobian elliptic function cd for the elliptic
|
||||
modulus k and the quarter-period units u.
|
||||
*/
|
||||
static Complex<double> cde (Complex<double> u, double k) noexcept;
|
||||
|
||||
/** Computes the Jacobian elliptic function sn for the elliptic
|
||||
modulus k and the quarter-period units u.
|
||||
*/
|
||||
static Complex<double> sne (Complex<double> u, double k) noexcept;
|
||||
|
||||
/** Computes the inverse of the Jacobian elliptic function sn
|
||||
for the elliptic modulus k and the quarter-period units u.
|
||||
*/
|
||||
static Complex<double> asne (Complex<double> w, double k) noexcept;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
58
deps/juce/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp
vendored
Normal file
58
deps/juce/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
DEFINE_AVX_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
DEFINE_AVX_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast<int32_t>(0x80000000), 0, static_cast<int32_t>(0x80000000), 0, static_cast<int32_t>(0x80000000), 0, static_cast<int32_t>(0x80000000), 0 };
|
||||
DEFINE_AVX_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1, -1, -1, -1 };
|
||||
DEFINE_AVX_SIMD_CONST (int64_t, double, kEvenHighBit) = { static_cast<int64_t> (0x8000000000000000), 0, static_cast<int64_t> (0x8000000000000000), 0 };
|
||||
DEFINE_AVX_SIMD_CONST (double, double, kOne) = { 1.0, 1.0, 1.0, 1.0 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
DEFINE_AVX_SIMD_CONST (uint8_t, uint8_t, kHighBit) = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff };
|
||||
DEFINE_AVX_SIMD_CONST (uint16_t, uint16_t, kHighBit) = { 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
|
||||
DEFINE_AVX_SIMD_CONST (uint32_t, uint32_t, kHighBit) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1LL, -1LL, -1LL, -1LL };
|
||||
|
||||
DEFINE_AVX_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL };
|
||||
DEFINE_AVX_SIMD_CONST (uint64_t, uint64_t, kHighBit) = { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL };
|
||||
}
|
||||
}
|
||||
661
deps/juce/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h
vendored
Normal file
661
deps/juce/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h
vendored
Normal file
@@ -0,0 +1,661 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wignored-attributes")
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DECLARE_AVX_SIMD_CONST(type, name) \
|
||||
static __declspec(align(32)) const type name[32 / sizeof (type)]
|
||||
|
||||
#define DEFINE_AVX_SIMD_CONST(type, class_type, name) \
|
||||
__declspec(align(32)) const type SIMDNativeOps<class_type>:: name[32 / sizeof (type)]
|
||||
|
||||
#else
|
||||
#define DECLARE_AVX_SIMD_CONST(type, name) \
|
||||
static const type name[32 / sizeof (type)] __attribute__((aligned(32)))
|
||||
|
||||
#define DEFINE_AVX_SIMD_CONST(type, class_type, name) \
|
||||
const type SIMDNativeOps<class_type>:: name[32 / sizeof (type)] __attribute__((aligned(32)))
|
||||
|
||||
#endif
|
||||
|
||||
template <typename type>
|
||||
struct SIMDNativeOps;
|
||||
|
||||
//==============================================================================
|
||||
/** Single-precision floating point AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<float>
|
||||
{
|
||||
using vSIMDType = __m256;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (int32_t, kAllBitsSet);
|
||||
DECLARE_AVX_SIMD_CONST (int32_t, kEvenHighBit);
|
||||
DECLARE_AVX_SIMD_CONST (float, kOne);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE vconst (const float* a) noexcept { return load (a); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE vconst (const int32_t* a) noexcept { return _mm256_castsi256_ps (_mm256_load_si256 (reinterpret_cast <const __m256i*> (a))); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE expand (float s) noexcept { return _mm256_broadcast_ss (&s); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE load (const float* a) noexcept { return _mm256_load_ps (a); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256 value, float* dest) noexcept { _mm256_store_ps (dest, value); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE add (__m256 a, __m256 b) noexcept { return _mm256_add_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE sub (__m256 a, __m256 b) noexcept { return _mm256_sub_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE mul (__m256 a, __m256 b) noexcept { return _mm256_mul_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE bit_and (__m256 a, __m256 b) noexcept { return _mm256_and_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE bit_or (__m256 a, __m256 b) noexcept { return _mm256_or_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE bit_xor (__m256 a, __m256 b) noexcept { return _mm256_xor_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE bit_notand (__m256 a, __m256 b) noexcept { return _mm256_andnot_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE bit_not (__m256 a) noexcept { return bit_notand (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE min (__m256 a, __m256 b) noexcept { return _mm256_min_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE max (__m256 a, __m256 b) noexcept { return _mm256_max_ps (a, b); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE equal (__m256 a, __m256 b) noexcept { return _mm256_cmp_ps (a, b, _CMP_EQ_OQ); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE notEqual (__m256 a, __m256 b) noexcept { return _mm256_cmp_ps (a, b, _CMP_NEQ_OQ); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE greaterThan (__m256 a, __m256 b) noexcept { return _mm256_cmp_ps (a, b, _CMP_GT_OQ); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256 a, __m256 b) noexcept { return _mm256_cmp_ps (a, b, _CMP_GE_OQ); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256 a, __m256 b) noexcept { return (_mm256_movemask_ps (equal (a, b)) == 0xff); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE dupeven (__m256 a) noexcept { return _mm256_shuffle_ps (a, a, _MM_SHUFFLE (2, 2, 0, 0)); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE dupodd (__m256 a) noexcept { return _mm256_shuffle_ps (a, a, _MM_SHUFFLE (3, 3, 1, 1)); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE swapevenodd (__m256 a) noexcept { return _mm256_shuffle_ps (a, a, _MM_SHUFFLE (2, 3, 0, 1)); }
|
||||
static forcedinline float JUCE_VECTOR_CALLTYPE get (__m256 v, size_t i) noexcept { return SIMDFallbackOps<float, __m256>::get (v, i); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE set (__m256 v, size_t i, float s) noexcept { return SIMDFallbackOps<float, __m256>::set (v, i, s); }
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE truncate (__m256 a) noexcept { return _mm256_cvtepi32_ps (_mm256_cvttps_epi32 (a)); }
|
||||
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE multiplyAdd (__m256 a, __m256 b, __m256 c) noexcept
|
||||
{
|
||||
#if __FMA__
|
||||
return _mm256_fmadd_ps (b, c, a);
|
||||
#else
|
||||
return add (a, mul (b, c));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE oddevensum (__m256 a) noexcept
|
||||
{
|
||||
a = _mm256_add_ps (_mm256_shuffle_ps (a, a, _MM_SHUFFLE (1, 0, 3, 2)), a);
|
||||
return add (_mm256_permute2f128_ps (a, a, 1), a);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE cmplxmul (__m256 a, __m256 b) noexcept
|
||||
{
|
||||
__m256 rr_ir = mul (a, dupeven (b));
|
||||
__m256 ii_ri = mul (swapevenodd (a), dupodd (b));
|
||||
return add (rr_ir, bit_xor (ii_ri, vconst (kEvenHighBit)));
|
||||
}
|
||||
|
||||
static forcedinline float JUCE_VECTOR_CALLTYPE sum (__m256 a) noexcept
|
||||
{
|
||||
__m256 retval = _mm256_dp_ps (a, vconst (kOne), 0xff);
|
||||
__m256 tmp = _mm256_permute2f128_ps (retval, retval, 1);
|
||||
retval = _mm256_add_ps (retval, tmp);
|
||||
|
||||
#if JUCE_GCC
|
||||
return retval[0];
|
||||
#else
|
||||
return _mm256_cvtss_f32 (retval);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Double-precision floating point AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<double>
|
||||
{
|
||||
using vSIMDType = __m256d;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (int64_t, kAllBitsSet);
|
||||
DECLARE_AVX_SIMD_CONST (int64_t, kEvenHighBit);
|
||||
DECLARE_AVX_SIMD_CONST (double, kOne);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE vconst (const double* a) noexcept { return load (a); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return _mm256_castsi256_pd (_mm256_load_si256 (reinterpret_cast <const __m256i*> (a))); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE expand (double s) noexcept { return _mm256_broadcast_sd (&s); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE load (const double* a) noexcept { return _mm256_load_pd (a); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256d value, double* dest) noexcept { _mm256_store_pd (dest, value); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE add (__m256d a, __m256d b) noexcept { return _mm256_add_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE sub (__m256d a, __m256d b) noexcept { return _mm256_sub_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE mul (__m256d a, __m256d b) noexcept { return _mm256_mul_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE bit_and (__m256d a, __m256d b) noexcept { return _mm256_and_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE bit_or (__m256d a, __m256d b) noexcept { return _mm256_or_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE bit_xor (__m256d a, __m256d b) noexcept { return _mm256_xor_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE bit_notand (__m256d a, __m256d b) noexcept { return _mm256_andnot_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE bit_not (__m256d a) noexcept { return bit_notand (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE min (__m256d a, __m256d b) noexcept { return _mm256_min_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE max (__m256d a, __m256d b) noexcept { return _mm256_max_pd (a, b); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE equal (__m256d a, __m256d b) noexcept { return _mm256_cmp_pd (a, b, _CMP_EQ_OQ); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE notEqual (__m256d a, __m256d b) noexcept { return _mm256_cmp_pd (a, b, _CMP_NEQ_OQ); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE greaterThan (__m256d a, __m256d b) noexcept { return _mm256_cmp_pd (a, b, _CMP_GT_OQ); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256d a, __m256d b) noexcept { return _mm256_cmp_pd (a, b, _CMP_GE_OQ); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256d a, __m256d b) noexcept { return (_mm256_movemask_pd (equal (a, b)) == 0xf); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE multiplyAdd (__m256d a, __m256d b, __m256d c) noexcept { return _mm256_add_pd (a, _mm256_mul_pd (b, c)); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE dupeven (__m256d a) noexcept { return _mm256_shuffle_pd (a, a, 0); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE dupodd (__m256d a) noexcept { return _mm256_shuffle_pd (a, a, (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE swapevenodd (__m256d a) noexcept { return _mm256_shuffle_pd (a, a, (1 << 0) | (0 << 1) | (1 << 2) | (0 << 3)); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE oddevensum (__m256d a) noexcept { return _mm256_add_pd (_mm256_permute2f128_pd (a, a, 1), a); }
|
||||
static forcedinline double JUCE_VECTOR_CALLTYPE get (__m256d v, size_t i) noexcept { return SIMDFallbackOps<double, __m256d>::get (v, i); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE set (__m256d v, size_t i, double s) noexcept { return SIMDFallbackOps<double, __m256d>::set (v, i, s); }
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE truncate (__m256d a) noexcept { return _mm256_cvtepi32_pd (_mm256_cvttpd_epi32 (a)); }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE cmplxmul (__m256d a, __m256d b) noexcept
|
||||
{
|
||||
__m256d rr_ir = mul (a, dupeven (b));
|
||||
__m256d ii_ri = mul (swapevenodd (a), dupodd (b));
|
||||
return add (rr_ir, bit_xor (ii_ri, vconst (kEvenHighBit)));
|
||||
}
|
||||
|
||||
static forcedinline double JUCE_VECTOR_CALLTYPE sum (__m256d a) noexcept
|
||||
{
|
||||
__m256d retval = _mm256_hadd_pd (a, a);
|
||||
__m256d tmp = _mm256_permute2f128_pd (retval, retval, 1);
|
||||
retval = _mm256_add_pd (retval, tmp);
|
||||
|
||||
#if JUCE_GCC
|
||||
return retval[0];
|
||||
#else
|
||||
return _mm256_cvtsd_f64 (retval);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 8-bit integer AVX intrinsics
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int8_t>
|
||||
{
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (int8_t, kAllBitsSet);
|
||||
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int8_t s) noexcept { return _mm256_set1_epi8 (s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const int8_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, int8_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { return _mm256_min_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { return _mm256_max_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return _mm256_movemask_epi8 (equal (a, b)) == -1; }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline int8_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<int8_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, int8_t s) noexcept { return SIMDFallbackOps<int8_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline int8_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept
|
||||
{
|
||||
__m256i lo = _mm256_unpacklo_epi8 (a, _mm256_setzero_si256());
|
||||
__m256i hi = _mm256_unpackhi_epi8 (a, _mm256_setzero_si256());
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
lo = _mm256_hadd_epi16 (lo, lo);
|
||||
hi = _mm256_hadd_epi16 (hi, hi);
|
||||
}
|
||||
|
||||
#if JUCE_GCC
|
||||
return (int8_t) ((lo[0] & 0xff) +
|
||||
(hi[0] & 0xff) +
|
||||
(lo[2] & 0xff) +
|
||||
(hi[2] & 0xff));
|
||||
#else
|
||||
constexpr int mask = (2 << 0) | (3 << 2) | (0 << 4) | (1 << 6);
|
||||
|
||||
return (int8_t) ((_mm256_cvtsi256_si32 (lo) & 0xff) +
|
||||
(_mm256_cvtsi256_si32 (hi) & 0xff) +
|
||||
(_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (lo, mask)) & 0xff) +
|
||||
(_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (hi, mask)) & 0xff));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b)
|
||||
{
|
||||
// unpack and multiply
|
||||
__m256i even = _mm256_mullo_epi16 (a, b);
|
||||
__m256i odd = _mm256_mullo_epi16 (_mm256_srli_epi16 (a, 8), _mm256_srli_epi16 (b, 8));
|
||||
|
||||
return _mm256_or_si256 (_mm256_slli_epi16 (odd, 8),
|
||||
_mm256_srli_epi16 (_mm256_slli_epi16 (even, 8), 8));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 8-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint8_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (uint8_t, kHighBit);
|
||||
DECLARE_AVX_SIMD_CONST (uint8_t, kAllBitsSet);
|
||||
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, load (kHighBit)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (uint8_t s) noexcept { return _mm256_set1_epi8 ((int8_t) s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const uint8_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, uint8_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { return _mm256_min_epu8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { return _mm256_max_epu8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi8 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi8 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline uint8_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<uint8_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, uint8_t s) noexcept { return SIMDFallbackOps<uint8_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline uint8_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept
|
||||
{
|
||||
__m256i lo = _mm256_unpacklo_epi8 (a, _mm256_setzero_si256());
|
||||
__m256i hi = _mm256_unpackhi_epi8 (a, _mm256_setzero_si256());
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
lo = _mm256_hadd_epi16 (lo, lo);
|
||||
hi = _mm256_hadd_epi16 (hi, hi);
|
||||
}
|
||||
|
||||
#if JUCE_GCC
|
||||
return (uint8_t) ((static_cast<uint32_t> (lo[0]) & 0xffu) +
|
||||
(static_cast<uint32_t> (hi[0]) & 0xffu) +
|
||||
(static_cast<uint32_t> (lo[2]) & 0xffu) +
|
||||
(static_cast<uint32_t> (hi[2]) & 0xffu));
|
||||
#else
|
||||
constexpr int mask = (2 << 0) | (3 << 2) | (0 << 4) | (1 << 6);
|
||||
|
||||
return (uint8_t) ((static_cast<uint32_t> (_mm256_cvtsi256_si32 (lo)) & 0xffu) +
|
||||
(static_cast<uint32_t> (_mm256_cvtsi256_si32 (hi)) & 0xffu) +
|
||||
(static_cast<uint32_t> (_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (lo, mask))) & 0xffu) +
|
||||
(static_cast<uint32_t> (_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (hi, mask))) & 0xffu));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b)
|
||||
{
|
||||
// unpack and multiply
|
||||
__m256i even = _mm256_mullo_epi16 (a, b);
|
||||
__m256i odd = _mm256_mullo_epi16 (_mm256_srli_epi16 (a, 8), _mm256_srli_epi16 (b, 8));
|
||||
|
||||
return _mm256_or_si256 (_mm256_slli_epi16 (odd, 8),
|
||||
_mm256_srli_epi16 (_mm256_slli_epi16 (even, 8), 8));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 16-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int16_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (int16_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int16_t s) noexcept { return _mm256_set1_epi16 (s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const int16_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, int16_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept { return _mm256_mullo_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { return _mm256_min_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { return _mm256_max_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline int16_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<int16_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, int16_t s) noexcept { return SIMDFallbackOps<int16_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline int16_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept
|
||||
{
|
||||
__m256i tmp = _mm256_hadd_epi16 (a, a);
|
||||
tmp = _mm256_hadd_epi16 (tmp, tmp);
|
||||
tmp = _mm256_hadd_epi16 (tmp, tmp);
|
||||
|
||||
#if JUCE_GCC
|
||||
return (int16_t) ((tmp[0] & 0xffff) + (tmp[2] & 0xffff));
|
||||
#else
|
||||
constexpr int mask = (2 << 0) | (3 << 2) | (0 << 4) | (1 << 6);
|
||||
|
||||
return (int16_t) ((_mm256_cvtsi256_si32 (tmp) & 0xffff) +
|
||||
(_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (tmp, mask)) & 0xffff));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 16-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint16_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (uint16_t, kHighBit);
|
||||
DECLARE_AVX_SIMD_CONST (uint16_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, load (kHighBit)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (uint16_t s) noexcept { return _mm256_set1_epi16 ((int16_t) s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const uint16_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, uint16_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept { return _mm256_mullo_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { return _mm256_min_epu16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { return _mm256_max_epu16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi16 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi16 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline uint16_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<uint16_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, uint16_t s) noexcept { return SIMDFallbackOps<uint16_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline uint16_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept
|
||||
{
|
||||
__m256i tmp = _mm256_hadd_epi16 (a, a);
|
||||
tmp = _mm256_hadd_epi16 (tmp, tmp);
|
||||
tmp = _mm256_hadd_epi16 (tmp, tmp);
|
||||
|
||||
#if JUCE_GCC
|
||||
return (uint16_t) ((static_cast<uint32_t> (tmp[0]) & 0xffffu) +
|
||||
(static_cast<uint32_t> (tmp[2]) & 0xffffu));
|
||||
#else
|
||||
constexpr int mask = (2 << 0) | (3 << 2) | (0 << 4) | (1 << 6);
|
||||
|
||||
return (uint16_t) ((static_cast<uint32_t> (_mm256_cvtsi256_si32 (tmp)) & 0xffffu) +
|
||||
(static_cast<uint32_t> (_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (tmp, mask))) & 0xffffu));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 32-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int32_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (int32_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int32_t s) noexcept { return _mm256_set1_epi32 (s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const int32_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, int32_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept { return _mm256_mullo_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { return _mm256_min_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { return _mm256_max_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline int32_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<int32_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, int32_t s) noexcept { return SIMDFallbackOps<int32_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline int32_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept
|
||||
{
|
||||
__m256i tmp = _mm256_hadd_epi32 (a, a);
|
||||
tmp = _mm256_hadd_epi32 (tmp, tmp);
|
||||
|
||||
#if JUCE_GCC
|
||||
return (int32_t) (tmp[0] + tmp[2]);
|
||||
#else
|
||||
constexpr int mask = (2 << 0) | (3 << 2) | (0 << 4) | (1 << 6);
|
||||
|
||||
return _mm256_cvtsi256_si32 (tmp) + _mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (tmp, mask));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 32-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint32_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (uint32_t, kAllBitsSet);
|
||||
DECLARE_AVX_SIMD_CONST (uint32_t, kHighBit);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, load (kHighBit)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (uint32_t s) noexcept { return _mm256_set1_epi32 ((int32_t) s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const uint32_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, uint32_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept { return _mm256_mullo_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { return _mm256_min_epu32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { return _mm256_max_epu32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi32 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi32 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline uint32_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<uint32_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, uint32_t s) noexcept { return SIMDFallbackOps<uint32_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline uint32_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept
|
||||
{
|
||||
__m256i tmp = _mm256_hadd_epi32 (a, a);
|
||||
tmp = _mm256_hadd_epi32 (tmp, tmp);
|
||||
|
||||
#if JUCE_GCC
|
||||
return static_cast<uint32_t> (tmp[0]) + static_cast<uint32_t> (tmp[2]);
|
||||
#else
|
||||
constexpr int mask = (2 << 0) | (3 << 2) | (0 << 4) | (1 << 6);
|
||||
|
||||
return static_cast<uint32_t> (_mm256_cvtsi256_si32 (tmp))
|
||||
+ static_cast<uint32_t> (_mm256_cvtsi256_si32 (_mm256_permute4x64_epi64 (tmp, mask)));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 64-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int64_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (int64_t, kAllBitsSet);
|
||||
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int64_t s) noexcept { return _mm256_set1_epi64x ((int64_t) s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const int64_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, int64_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { __m256i lt = greaterThan (b, a); return bit_or (bit_and (lt, a), bit_andnot (lt, b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { __m256i gt = greaterThan (a, b); return bit_or (bit_and (gt, a), bit_andnot (gt, b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline int64_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<int64_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, int64_t s) noexcept { return SIMDFallbackOps<int64_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline int64_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept { return SIMDFallbackOps<int64_t, __m256i>::sum (a); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept { return SIMDFallbackOps<int64_t, __m256i>::mul (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 64-bit integer AVX intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint64_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m256i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_AVX_SIMD_CONST (uint64_t, kAllBitsSet);
|
||||
DECLARE_AVX_SIMD_CONST (uint64_t, kHighBit);
|
||||
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (uint64_t s) noexcept { return _mm256_set1_epi64x ((int64_t) s); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE load (const uint64_t* p) noexcept { return _mm256_load_si256 (reinterpret_cast<const __m256i*> (p)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m256i value, uint64_t* dest) noexcept { _mm256_store_si256 (reinterpret_cast<__m256i*> (dest), value); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, load (kHighBit)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE add (__m256i a, __m256i b) noexcept { return _mm256_add_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE sub (__m256i a, __m256i b) noexcept { return _mm256_sub_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_and (__m256i a, __m256i b) noexcept { return _mm256_and_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_or (__m256i a, __m256i b) noexcept { return _mm256_or_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_xor (__m256i a, __m256i b) noexcept { return _mm256_xor_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_andnot (__m256i a, __m256i b) noexcept { return _mm256_andnot_si256 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE bit_not (__m256i a) noexcept { return _mm256_andnot_si256 (a, load (kAllBitsSet)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE min (__m256i a, __m256i b) noexcept { __m256i lt = greaterThan (b, a); return bit_or (bit_and (lt, a), bit_andnot (lt, b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE max (__m256i a, __m256i b) noexcept { __m256i gt = greaterThan (a, b); return bit_or (bit_and (gt, a), bit_andnot (gt, b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE equal (__m256i a, __m256i b) noexcept { return _mm256_cmpeq_epi64 (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThan (__m256i a, __m256i b) noexcept { return _mm256_cmpgt_epi64 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m256i a, __m256i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE multiplyAdd (__m256i a, __m256i b, __m256i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE notEqual (__m256i a, __m256i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m256i a, __m256i b) noexcept { return (_mm256_movemask_epi8 (equal (a, b)) == -1); }
|
||||
static forcedinline uint64_t JUCE_VECTOR_CALLTYPE get (__m256i v, size_t i) noexcept { return SIMDFallbackOps<uint64_t, __m256i>::get (v, i); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE set (__m256i v, size_t i, uint64_t s) noexcept { return SIMDFallbackOps<uint64_t, __m256i>::set (v, i, s); }
|
||||
static forcedinline uint64_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept { return SIMDFallbackOps<uint64_t, __m256i>::sum (a); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept { return SIMDFallbackOps<uint64_t, __m256i>::mul (a, b); }
|
||||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE truncate (__m256i a) noexcept { return a; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
265
deps/juce/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h
vendored
Normal file
265
deps/juce/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** A template specialisation to find corresponding mask type for primitives. */
|
||||
namespace SIMDInternal
|
||||
{
|
||||
template <typename Primitive> struct MaskTypeFor { using type = Primitive; };
|
||||
template <> struct MaskTypeFor <float> { using type = uint32_t; };
|
||||
template <> struct MaskTypeFor <double> { using type = uint64_t; };
|
||||
template <> struct MaskTypeFor <char> { using type = uint8_t; };
|
||||
template <> struct MaskTypeFor <int8_t> { using type = uint8_t; };
|
||||
template <> struct MaskTypeFor <int16_t> { using type = uint16_t; };
|
||||
template <> struct MaskTypeFor <int32_t> { using type = uint32_t; };
|
||||
template <> struct MaskTypeFor <int64_t> { using type = uint64_t; };
|
||||
template <> struct MaskTypeFor <std::complex<float>> { using type = uint32_t; };
|
||||
template <> struct MaskTypeFor <std::complex<double>> { using type = uint64_t; };
|
||||
|
||||
template <typename Primitive> struct PrimitiveType { using type = typename std::remove_cv<Primitive>::type; };
|
||||
template <typename Primitive> struct PrimitiveType<std::complex<Primitive>> { using type = typename std::remove_cv<Primitive>::type; };
|
||||
|
||||
template <int n> struct Log2Helper { enum { value = Log2Helper<n/2>::value + 1 }; };
|
||||
template <> struct Log2Helper<1> { enum { value = 0 }; };
|
||||
}
|
||||
|
||||
/**
|
||||
Useful fallback routines to use if the native SIMD op is not supported. You
|
||||
should never need to use this directly. Use juce_SIMDRegister instead.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename ScalarType, typename vSIMDType>
|
||||
struct SIMDFallbackOps
|
||||
{
|
||||
static constexpr size_t n = sizeof (vSIMDType) / sizeof (ScalarType);
|
||||
static constexpr size_t mask = (sizeof (vSIMDType) / sizeof (ScalarType)) - 1;
|
||||
static constexpr size_t bits = SIMDInternal::Log2Helper<(int) n>::value;
|
||||
|
||||
// helper types
|
||||
using MaskType = typename SIMDInternal::MaskTypeFor<ScalarType>::type;
|
||||
union UnionType { vSIMDType v; ScalarType s[n]; };
|
||||
union UnionMaskType { vSIMDType v; MaskType m[n]; };
|
||||
|
||||
|
||||
// fallback methods
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return apply<ScalarAdd> (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return apply<ScalarSub> (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return apply<ScalarMul> (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return bitapply<ScalarAnd> (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return bitapply<ScalarOr > (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return bitapply<ScalarXor> (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return bitapply<ScalarNot> (a, b); }
|
||||
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return apply<ScalarMin> (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return apply<ScalarMax> (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return cmp<ScalarEq > (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return cmp<ScalarNeq> (a, b); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return cmp<ScalarGt > (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return cmp<ScalarGeq> (a, b); }
|
||||
|
||||
static forcedinline ScalarType get (vSIMDType v, size_t i) noexcept
|
||||
{
|
||||
UnionType u {v};
|
||||
return u.s[i];
|
||||
}
|
||||
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, ScalarType s) noexcept
|
||||
{
|
||||
UnionType u {v};
|
||||
|
||||
u.s[i] = s;
|
||||
return u.v;
|
||||
}
|
||||
|
||||
static forcedinline vSIMDType bit_not (vSIMDType av) noexcept
|
||||
{
|
||||
UnionMaskType a {av};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
a.m[i] = ~a.m[i];
|
||||
|
||||
return a.v;
|
||||
}
|
||||
|
||||
static forcedinline ScalarType sum (vSIMDType av) noexcept
|
||||
{
|
||||
UnionType a {av};
|
||||
auto retval = static_cast<ScalarType> (0);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
retval = static_cast<ScalarType> (retval + a.s[i]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static forcedinline vSIMDType truncate (vSIMDType av) noexcept
|
||||
{
|
||||
UnionType a {av};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
a.s[i] = static_cast<ScalarType> (static_cast<int> (a.s[i]));
|
||||
|
||||
return a.v;
|
||||
}
|
||||
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType av, vSIMDType bv, vSIMDType cv) noexcept
|
||||
{
|
||||
UnionType a {av}, b {bv}, c {cv};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
a.s[i] += b.s[i] * c.s[i];
|
||||
|
||||
return a.v;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline bool allEqual (vSIMDType av, vSIMDType bv) noexcept
|
||||
{
|
||||
UnionType a {av}, b {bv};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
if (a.s[i] != b.s[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType cmplxmul (vSIMDType av, vSIMDType bv) noexcept
|
||||
{
|
||||
UnionType a {av}, b {bv}, r;
|
||||
|
||||
const int m = n >> 1;
|
||||
for (int i = 0; i < m; ++i)
|
||||
{
|
||||
std::complex<ScalarType> result
|
||||
= std::complex<ScalarType> (a.s[i<<1], a.s[(i<<1)|1])
|
||||
* std::complex<ScalarType> (b.s[i<<1], b.s[(i<<1)|1]);
|
||||
|
||||
r.s[i<<1] = result.real();
|
||||
r.s[(i<<1)|1] = result.imag();
|
||||
}
|
||||
|
||||
return r.v;
|
||||
}
|
||||
|
||||
struct ScalarAdd { static forcedinline ScalarType op (ScalarType a, ScalarType b) noexcept { return a + b; } };
|
||||
struct ScalarSub { static forcedinline ScalarType op (ScalarType a, ScalarType b) noexcept { return a - b; } };
|
||||
struct ScalarMul { static forcedinline ScalarType op (ScalarType a, ScalarType b) noexcept { return a * b; } };
|
||||
struct ScalarMin { static forcedinline ScalarType op (ScalarType a, ScalarType b) noexcept { return jmin (a, b); } };
|
||||
struct ScalarMax { static forcedinline ScalarType op (ScalarType a, ScalarType b) noexcept { return jmax (a, b); } };
|
||||
struct ScalarAnd { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return a & b; } };
|
||||
struct ScalarOr { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return a | b; } };
|
||||
struct ScalarXor { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return a ^ b; } };
|
||||
struct ScalarNot { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return (~a) & b; } };
|
||||
struct ScalarEq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a == b); } };
|
||||
struct ScalarNeq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a != b); } };
|
||||
struct ScalarGt { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a > b); } };
|
||||
struct ScalarGeq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a >= b); } };
|
||||
|
||||
// generic apply routines for operations above
|
||||
template <typename Op>
|
||||
static forcedinline vSIMDType apply (vSIMDType av, vSIMDType bv) noexcept
|
||||
{
|
||||
UnionType a {av}, b {bv};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
a.s[i] = Op::op (a.s[i], b.s[i]);
|
||||
|
||||
return a.v;
|
||||
}
|
||||
|
||||
template <typename Op>
|
||||
static forcedinline vSIMDType cmp (vSIMDType av, vSIMDType bv) noexcept
|
||||
{
|
||||
UnionType a {av}, b {bv};
|
||||
UnionMaskType r;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
r.m[i] = Op::op (a.s[i], b.s[i]) ? static_cast<MaskType> (-1) : static_cast<MaskType> (0);
|
||||
|
||||
return r.v;
|
||||
}
|
||||
|
||||
template <typename Op>
|
||||
static forcedinline vSIMDType bitapply (vSIMDType av, vSIMDType bv) noexcept
|
||||
{
|
||||
UnionMaskType a {av}, b {bv};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
a.m[i] = Op::op (a.m[i], b.m[i]);
|
||||
|
||||
return a.v;
|
||||
}
|
||||
|
||||
static forcedinline vSIMDType expand (ScalarType s) noexcept
|
||||
{
|
||||
UnionType r;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
r.s[i] = s;
|
||||
|
||||
return r.v;
|
||||
}
|
||||
|
||||
static forcedinline vSIMDType load (const ScalarType* a) noexcept
|
||||
{
|
||||
UnionType r;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
r.s[i] = a[i];
|
||||
|
||||
return r.v;
|
||||
}
|
||||
|
||||
static forcedinline void store (vSIMDType av, ScalarType* dest) noexcept
|
||||
{
|
||||
UnionType a {av};
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
dest[i] = a.s[i];
|
||||
}
|
||||
|
||||
template <unsigned int shuffle_idx>
|
||||
static forcedinline vSIMDType shuffle (vSIMDType av) noexcept
|
||||
{
|
||||
UnionType a {av}, r;
|
||||
|
||||
// the compiler will unroll this loop and the index can
|
||||
// be computed at compile-time, so this will be super fast
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
r.s[i] = a.s[(shuffle_idx >> (bits * i)) & mask];
|
||||
|
||||
return r.v;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
43
deps/juce/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp
vendored
Normal file
43
deps/juce/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
DEFINE_NEON_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1 };
|
||||
DEFINE_NEON_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast<int32_t>(0x80000000), 0, static_cast<int32_t>(0x80000000), 0 };
|
||||
DEFINE_NEON_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
DEFINE_NEON_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
DEFINE_NEON_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
DEFINE_NEON_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
DEFINE_NEON_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff };
|
||||
DEFINE_NEON_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1 };
|
||||
DEFINE_NEON_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
|
||||
DEFINE_NEON_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1, -1 };
|
||||
DEFINE_NEON_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffff, 0xffffffffffffffff };
|
||||
}
|
||||
}
|
||||
501
deps/juce/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h
vendored
Normal file
501
deps/juce/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h
vendored
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wignored-attributes")
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DECLARE_NEON_SIMD_CONST(type, name) \
|
||||
static __declspec(align(16)) const type name [16 / sizeof (type)]
|
||||
|
||||
#define DEFINE_NEON_SIMD_CONST(type, class_type, name) \
|
||||
__declspec(align(16)) const type SIMDNativeOps<class_type>:: name [16 / sizeof (type)]
|
||||
|
||||
#else
|
||||
#define DECLARE_NEON_SIMD_CONST(type, name) \
|
||||
static const type name [16 / sizeof (type)] __attribute__((aligned(16)))
|
||||
|
||||
#define DEFINE_NEON_SIMD_CONST(type, class_type, name) \
|
||||
const type SIMDNativeOps<class_type>:: name [16 / sizeof (type)] __attribute__((aligned(16)))
|
||||
|
||||
#endif
|
||||
|
||||
template <typename type>
|
||||
struct SIMDNativeOps;
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 32-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint32_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = uint32x4_t;
|
||||
using fb = SIMDFallbackOps<uint32_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (uint32_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (uint32_t s) noexcept { return vdupq_n_u32 (s); }
|
||||
static forcedinline vSIMDType load (const uint32_t* a) noexcept { return vld1q_u32 (a); }
|
||||
static forcedinline void store (vSIMDType value, uint32_t* a) noexcept { vst1q_u32 (a, value); }
|
||||
static forcedinline uint32_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, uint32_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u32 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u32 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_u32 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_u32 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_u32 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_u32 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_u32 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_u32 ((uint32_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_u32 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_u32 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_u32 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (sum (notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_u32 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_u32 (a, b); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_u32 (a, b, c); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
|
||||
static forcedinline uint32_t sum (vSIMDType a) noexcept
|
||||
{
|
||||
auto rr = vadd_u32 (vget_high_u32 (a), vget_low_u32 (a));
|
||||
return vget_lane_u32 (vpadd_u32 (rr, rr), 0);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 32-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int32_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = int32x4_t;
|
||||
using fb = SIMDFallbackOps<int32_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (int32_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (int32_t s) noexcept { return vdupq_n_s32 (s); }
|
||||
static forcedinline vSIMDType load (const int32_t* a) noexcept { return vld1q_s32 (a); }
|
||||
static forcedinline void store (vSIMDType value, int32_t* a) noexcept { vst1q_s32 (a, value); }
|
||||
static forcedinline int32_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, int32_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s32 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s32 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_s32 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_s32 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_s32 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_s32 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_s32 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_s32 ((int32_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_s32 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_s32 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_s32 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (sum (notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_s32 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_s32 (a, b); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_s32 (a, b, c); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
|
||||
static forcedinline int32_t sum (vSIMDType a) noexcept
|
||||
{
|
||||
auto rr = vadd_s32 (vget_high_s32 (a), vget_low_s32 (a));
|
||||
rr = vpadd_s32 (rr, rr);
|
||||
return vget_lane_s32 (rr, 0);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 8-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int8_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = int8x16_t;
|
||||
using fb = SIMDFallbackOps<int8_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (int8_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (int8_t s) noexcept { return vdupq_n_s8 (s); }
|
||||
static forcedinline vSIMDType load (const int8_t* a) noexcept { return vld1q_s8 (a); }
|
||||
static forcedinline void store (vSIMDType value, int8_t* a) noexcept { vst1q_s8 (a, value); }
|
||||
static forcedinline int8_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, int8_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s8 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s8 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_s8 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_s8 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_s8 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_s8 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_s8 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_s8 ((int8_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_s8 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_s8 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_s8 (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_s8 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_s8 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<int32_t>::sum ((SIMDNativeOps<int32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_s8 (a, b, c); }
|
||||
static forcedinline int8_t sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 8-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint8_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = uint8x16_t;
|
||||
using fb = SIMDFallbackOps<uint8_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (uint8_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (uint8_t s) noexcept { return vdupq_n_u8 (s); }
|
||||
static forcedinline vSIMDType load (const uint8_t* a) noexcept { return vld1q_u8 (a); }
|
||||
static forcedinline void store (vSIMDType value, uint8_t* a) noexcept { vst1q_u8 (a, value); }
|
||||
static forcedinline uint8_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, uint8_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u8 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u8 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_u8 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_u8 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_u8 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_u8 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_u8 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_u8 ((uint8_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_u8 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_u8 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_u8 (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_u8 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_u8 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<uint32_t>::sum ((SIMDNativeOps<uint32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_u8 (a, b, c); }
|
||||
static forcedinline uint8_t sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 16-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int16_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = int16x8_t;
|
||||
using fb = SIMDFallbackOps<int16_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (int16_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (int16_t s) noexcept { return vdupq_n_s16 (s); }
|
||||
static forcedinline vSIMDType load (const int16_t* a) noexcept { return vld1q_s16 (a); }
|
||||
static forcedinline void store (vSIMDType value, int16_t* a) noexcept { vst1q_s16 (a, value); }
|
||||
static forcedinline int16_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, int16_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s16 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s16 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_s16 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_s16 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_s16 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_s16 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_s16 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_s16 ((int16_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_s16 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_s16 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_s16 (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_s16 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_s16 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<int32_t>::sum ((SIMDNativeOps<int32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_s16 (a, b, c); }
|
||||
static forcedinline int16_t sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 16-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint16_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = uint16x8_t;
|
||||
using fb = SIMDFallbackOps<uint16_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (uint16_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (uint16_t s) noexcept { return vdupq_n_u16 (s); }
|
||||
static forcedinline vSIMDType load (const uint16_t* a) noexcept { return vld1q_u16 (a); }
|
||||
static forcedinline void store (vSIMDType value, uint16_t* a) noexcept { vst1q_u16 (a, value); }
|
||||
static forcedinline uint16_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, uint16_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u16 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u16 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_u16 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_u16 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_u16 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_u16 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_u16 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_u16 ((uint16_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_u16 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_u16 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_u16 (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_u16 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_u16 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<uint32_t>::sum ((SIMDNativeOps<uint32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_u16 (a, b, c); }
|
||||
static forcedinline uint16_t sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 64-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int64_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = int64x2_t;
|
||||
using fb = SIMDFallbackOps<int64_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (int64_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (int64_t s) noexcept { return vdupq_n_s64 (s); }
|
||||
static forcedinline vSIMDType load (const int64_t* a) noexcept { return vld1q_s64 (a); }
|
||||
static forcedinline void store (vSIMDType value, int64_t* a) noexcept { vst1q_s64 (a, value); }
|
||||
static forcedinline int64_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, int64_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s64 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s64 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return fb::mul (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_s64 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_s64 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_s64 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_s64 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_s64 ((int64_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return fb::min (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return fb::max (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return fb::equal (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return fb::notEqual (a, b); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThan (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThanOrEqual (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<int32_t>::sum ((SIMDNativeOps<int32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return fb::multiplyAdd (a, b, c); }
|
||||
static forcedinline int64_t sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 64-bit integer NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint64_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = uint64x2_t;
|
||||
using fb = SIMDFallbackOps<uint64_t, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (uint64_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (uint64_t s) noexcept { return vdupq_n_u64 (s); }
|
||||
static forcedinline vSIMDType load (const uint64_t* a) noexcept { return vld1q_u64 (a); }
|
||||
static forcedinline void store (vSIMDType value, uint64_t* a) noexcept { vst1q_u64 (a, value); }
|
||||
static forcedinline uint64_t get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, uint64_t s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u64 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u64 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return fb::mul (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return vandq_u64 (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return vorrq_u64 (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return veorq_u64 (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return vbicq_u64 (b, a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_u64 ((uint64_t*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return fb::min (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return fb::max (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return fb::equal (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return fb::notEqual (a, b); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThan (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThanOrEqual (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<uint32_t>::sum ((SIMDNativeOps<uint32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return fb::multiplyAdd (a, b, c); }
|
||||
static forcedinline uint64_t sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Single-precision floating point NEON intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<float>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = float32x4_t;
|
||||
using vMaskType = uint32x4_t;
|
||||
using fb = SIMDFallbackOps<float, vSIMDType>;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_NEON_SIMD_CONST (int32_t, kAllBitsSet);
|
||||
DECLARE_NEON_SIMD_CONST (int32_t, kEvenHighBit);
|
||||
DECLARE_NEON_SIMD_CONST (float, kOne);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType expand (float s) noexcept { return vdupq_n_f32 (s); }
|
||||
static forcedinline vSIMDType load (const float* a) noexcept { return vld1q_f32 (a); }
|
||||
static forcedinline float get (vSIMDType v, size_t i) noexcept { return v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, float s) noexcept { v[i] = s; return v; }
|
||||
static forcedinline void store (vSIMDType value, float* a) noexcept { vst1q_f32 (a, value); }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_f32 (a, b); }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_f32 (a, b); }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_f32 (a, b); }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vandq_u32 ((vMaskType) a, (vMaskType) b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vorrq_u32 ((vMaskType) a, (vMaskType) b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) veorq_u32 ((vMaskType) a, (vMaskType) b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vbicq_u32 ((vMaskType) b, (vMaskType) a); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_f32 ((float*) kAllBitsSet)); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_f32 (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_f32 (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_f32 (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_f32 (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_f32 (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps<uint32_t>::sum ((SIMDNativeOps<uint32_t>::vSIMDType) notEqual (a, b)) == 0); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_f32 (a, b, c); }
|
||||
static forcedinline vSIMDType dupeven (vSIMDType a) noexcept { return fb::shuffle<(0 << 0) | (0 << 2) | (2 << 4) | (2 << 6)> (a); }
|
||||
static forcedinline vSIMDType dupodd (vSIMDType a) noexcept { return fb::shuffle<(1 << 0) | (1 << 2) | (3 << 4) | (3 << 6)> (a); }
|
||||
static forcedinline vSIMDType swapevenodd (vSIMDType a) noexcept { return fb::shuffle<(1 << 0) | (0 << 2) | (3 << 4) | (2 << 6)> (a); }
|
||||
static forcedinline vSIMDType oddevensum (vSIMDType a) noexcept { return add (fb::shuffle<(2 << 0) | (3 << 2) | (0 << 4) | (1 << 6)> (a), a); }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return vcvtq_f32_s32 (vcvtq_s32_f32 (a)); }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline vSIMDType cmplxmul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
vSIMDType rr_ir = mul (a, dupeven (b));
|
||||
vSIMDType ii_ri = mul (swapevenodd (a), dupodd (b));
|
||||
return add (rr_ir, bit_xor (ii_ri, vld1q_f32 ((float*) kEvenHighBit)));
|
||||
}
|
||||
|
||||
static forcedinline float sum (vSIMDType a) noexcept
|
||||
{
|
||||
auto rr = vadd_f32 (vget_high_f32 (a), vget_low_f32 (a));
|
||||
return vget_lane_f32 (vpadd_f32 (rr, rr), 0);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Double-precision floating point NEON intrinsics does not exist in NEON
|
||||
so we need to emulate this.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<double>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = struct { double v[2]; };
|
||||
using fb = SIMDFallbackOps<double, vSIMDType>;
|
||||
|
||||
static forcedinline vSIMDType expand (double s) noexcept { return {{s, s}}; }
|
||||
static forcedinline vSIMDType load (const double* a) noexcept { return {{a[0], a[1]}}; }
|
||||
static forcedinline void store (vSIMDType v, double* a) noexcept { a[0] = v.v[0]; a[1] = v.v[1]; }
|
||||
static forcedinline double get (vSIMDType v, size_t i) noexcept { return v.v[i]; }
|
||||
static forcedinline vSIMDType set (vSIMDType v, size_t i, double s) noexcept { v.v[i] = s; return v; }
|
||||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return {{a.v[0] + b.v[0], a.v[1] + b.v[1]}}; }
|
||||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return {{a.v[0] - b.v[0], a.v[1] - b.v[1]}}; }
|
||||
static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return {{a.v[0] * b.v[0], a.v[1] * b.v[1]}}; }
|
||||
static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return fb::bit_and (a, b); }
|
||||
static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return fb::bit_or (a, b); }
|
||||
static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return fb::bit_xor (a, b); }
|
||||
static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return fb::bit_notand (a, b); }
|
||||
static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return fb::bit_not (a); }
|
||||
static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return fb::min (a, b); }
|
||||
static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return fb::max (a, b); }
|
||||
static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return fb::equal (a, b); }
|
||||
static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return fb::notEqual (a, b); }
|
||||
static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThan (a, b); }
|
||||
static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThanOrEqual (a, b); }
|
||||
static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return fb::allEqual (a, b); }
|
||||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return fb::multiplyAdd (a, b, c); }
|
||||
static forcedinline vSIMDType cmplxmul (vSIMDType a, vSIMDType b) noexcept { return fb::cmplxmul (a, b); }
|
||||
static forcedinline double sum (vSIMDType a) noexcept { return fb::sum (a); }
|
||||
static forcedinline vSIMDType oddevensum (vSIMDType a) noexcept { return a; }
|
||||
static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return fb::truncate (a); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
58
deps/juce/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp
vendored
Normal file
58
deps/juce/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
DEFINE_SSE_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1 };
|
||||
DEFINE_SSE_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast<int32_t>(0x80000000), 0, static_cast<int32_t>(0x80000000), 0 };
|
||||
DEFINE_SSE_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1LL, -1LL };
|
||||
DEFINE_SSE_SIMD_CONST (int64_t, double, kEvenHighBit) = { static_cast<int64_t>(0x8000000000000000), 0 };
|
||||
DEFINE_SSE_SIMD_CONST (double, double, kOne) = { 1.0, 1.0 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
DEFINE_SSE_SIMD_CONST (uint8_t, uint8_t, kHighBit) = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff };
|
||||
DEFINE_SSE_SIMD_CONST (uint16_t, uint16_t, kHighBit) = { 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
|
||||
DEFINE_SSE_SIMD_CONST (uint32_t, uint32_t, kHighBit) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1, -1 };
|
||||
|
||||
DEFINE_SSE_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffff, 0xffffffffffffffff };
|
||||
DEFINE_SSE_SIMD_CONST (uint64_t, uint64_t, kHighBit) = { 0x8000000000000000, 0x8000000000000000 };
|
||||
}
|
||||
}
|
||||
729
deps/juce/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h
vendored
Normal file
729
deps/juce/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h
vendored
Normal file
@@ -0,0 +1,729 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wignored-attributes")
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DECLARE_SSE_SIMD_CONST(type, name) \
|
||||
static __declspec(align(16)) const type name [16 / sizeof (type)]
|
||||
|
||||
#define DEFINE_SSE_SIMD_CONST(type, class_type, name) \
|
||||
__declspec(align(16)) const type SIMDNativeOps<class_type>:: name [16 / sizeof (type)]
|
||||
|
||||
#else
|
||||
#define DECLARE_SSE_SIMD_CONST(type, name) \
|
||||
static const type name [16 / sizeof (type)] __attribute__((aligned(16)))
|
||||
|
||||
#define DEFINE_SSE_SIMD_CONST(type, class_type, name) \
|
||||
const type SIMDNativeOps<class_type>:: name [16 / sizeof (type)] __attribute__((aligned(16)))
|
||||
|
||||
#endif
|
||||
|
||||
template <typename type>
|
||||
struct SIMDNativeOps;
|
||||
|
||||
//==============================================================================
|
||||
/** Single-precision floating point SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<float>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (int32_t, kAllBitsSet);
|
||||
DECLARE_SSE_SIMD_CONST (int32_t, kEvenHighBit);
|
||||
DECLARE_SSE_SIMD_CONST (float, kOne);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE expand (float s) noexcept { return _mm_load1_ps (&s); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE load (const float* a) noexcept { return _mm_load_ps (a); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128 value, float* dest) noexcept { _mm_store_ps (dest, value); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE add (__m128 a, __m128 b) noexcept { return _mm_add_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE sub (__m128 a, __m128 b) noexcept { return _mm_sub_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE mul (__m128 a, __m128 b) noexcept { return _mm_mul_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_and (__m128 a, __m128 b) noexcept { return _mm_and_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_or (__m128 a, __m128 b) noexcept { return _mm_or_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_xor (__m128 a, __m128 b) noexcept { return _mm_xor_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_notand (__m128 a, __m128 b) noexcept { return _mm_andnot_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_not (__m128 a) noexcept { return bit_notand (a, _mm_loadu_ps ((float*) kAllBitsSet)); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE min (__m128 a, __m128 b) noexcept { return _mm_min_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE max (__m128 a, __m128 b) noexcept { return _mm_max_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE equal (__m128 a, __m128 b) noexcept { return _mm_cmpeq_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE notEqual (__m128 a, __m128 b) noexcept { return _mm_cmpneq_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE greaterThan (__m128 a, __m128 b) noexcept { return _mm_cmpgt_ps (a, b); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128 a, __m128 b) noexcept { return _mm_cmpge_ps (a, b); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128 a, __m128 b ) noexcept { return (_mm_movemask_ps (equal (a, b)) == 0xf); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE multiplyAdd (__m128 a, __m128 b, __m128 c) noexcept { return _mm_add_ps (a, _mm_mul_ps (b, c)); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE dupeven (__m128 a) noexcept { return _mm_shuffle_ps (a, a, _MM_SHUFFLE (2, 2, 0, 0)); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE dupodd (__m128 a) noexcept { return _mm_shuffle_ps (a, a, _MM_SHUFFLE (3, 3, 1, 1)); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE swapevenodd (__m128 a) noexcept { return _mm_shuffle_ps (a, a, _MM_SHUFFLE (2, 3, 0, 1)); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE oddevensum (__m128 a) noexcept { return _mm_add_ps (_mm_shuffle_ps (a, a, _MM_SHUFFLE (1, 0, 3, 2)), a); }
|
||||
static forcedinline float JUCE_VECTOR_CALLTYPE get (__m128 v, size_t i) noexcept { return SIMDFallbackOps<float, __m128>::get (v, i); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE set (__m128 v, size_t i, float s) noexcept { return SIMDFallbackOps<float, __m128>::set (v, i, s); }
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE truncate (__m128 a) noexcept { return _mm_cvtepi32_ps (_mm_cvttps_epi32 (a)); }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE cmplxmul (__m128 a, __m128 b) noexcept
|
||||
{
|
||||
__m128 rr_ir = mul (a, dupeven (b));
|
||||
__m128 ii_ri = mul (swapevenodd (a), dupodd (b));
|
||||
return add (rr_ir, bit_xor (ii_ri, _mm_loadu_ps ((float*) kEvenHighBit)));
|
||||
}
|
||||
|
||||
static forcedinline float JUCE_VECTOR_CALLTYPE sum (__m128 a) noexcept
|
||||
{
|
||||
#if defined(__SSE4__)
|
||||
__m128 retval = _mm_dp_ps (a, _mm_loadu_ps (kOne), 0xff);
|
||||
#elif defined(__SSE3__)
|
||||
__m128 retval = _mm_hadd_ps (_mm_hadd_ps (a, a), a);
|
||||
#else
|
||||
__m128 retval = _mm_add_ps (_mm_shuffle_ps (a, a, 0x4e), a);
|
||||
retval = _mm_add_ps (retval, _mm_shuffle_ps (retval, retval, 0xb1));
|
||||
#endif
|
||||
return _mm_cvtss_f32 (retval);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Double-precision floating point SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<double>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128d;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (int64_t, kAllBitsSet);
|
||||
DECLARE_SSE_SIMD_CONST (int64_t, kEvenHighBit);
|
||||
DECLARE_SSE_SIMD_CONST (double, kOne);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE vconst (const double* a) noexcept { return load (a); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return _mm_castsi128_pd (_mm_load_si128 (reinterpret_cast<const __m128i*> (a))); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE expand (double s) noexcept { return _mm_load1_pd (&s); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE load (const double* a) noexcept { return _mm_load_pd (a); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128d value, double* dest) noexcept { _mm_store_pd (dest, value); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE add (__m128d a, __m128d b) noexcept { return _mm_add_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE sub (__m128d a, __m128d b) noexcept { return _mm_sub_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE mul (__m128d a, __m128d b) noexcept { return _mm_mul_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_and (__m128d a, __m128d b) noexcept { return _mm_and_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_or (__m128d a, __m128d b) noexcept { return _mm_or_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_xor (__m128d a, __m128d b) noexcept { return _mm_xor_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_notand (__m128d a, __m128d b) noexcept { return _mm_andnot_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_not (__m128d a) noexcept { return bit_notand (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE min (__m128d a, __m128d b) noexcept { return _mm_min_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE max (__m128d a, __m128d b) noexcept { return _mm_max_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE equal (__m128d a, __m128d b) noexcept { return _mm_cmpeq_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE notEqual (__m128d a, __m128d b) noexcept { return _mm_cmpneq_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE greaterThan (__m128d a, __m128d b) noexcept { return _mm_cmpgt_pd (a, b); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128d a, __m128d b) noexcept { return _mm_cmpge_pd (a, b); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128d a, __m128d b ) noexcept { return (_mm_movemask_pd (equal (a, b)) == 0x3); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE multiplyAdd (__m128d a, __m128d b, __m128d c) noexcept { return _mm_add_pd (a, _mm_mul_pd (b, c)); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE dupeven (__m128d a) noexcept { return _mm_shuffle_pd (a, a, _MM_SHUFFLE2 (0, 0)); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE dupodd (__m128d a) noexcept { return _mm_shuffle_pd (a, a, _MM_SHUFFLE2 (1, 1)); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE swapevenodd (__m128d a) noexcept { return _mm_shuffle_pd (a, a, _MM_SHUFFLE2 (0, 1)); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE oddevensum (__m128d a) noexcept { return a; }
|
||||
static forcedinline double JUCE_VECTOR_CALLTYPE get (__m128d v, size_t i) noexcept { return SIMDFallbackOps<double, __m128d>::get (v, i); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE set (__m128d v, size_t i, double s) noexcept { return SIMDFallbackOps<double, __m128d>::set (v, i, s); }
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE truncate (__m128d a) noexcept { return _mm_cvtepi32_pd (_mm_cvttpd_epi32 (a)); }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE cmplxmul (__m128d a, __m128d b) noexcept
|
||||
{
|
||||
__m128d rr_ir = mul (a, dupeven (b));
|
||||
__m128d ii_ri = mul (swapevenodd (a), dupodd (b));
|
||||
return add (rr_ir, bit_xor (ii_ri, vconst (kEvenHighBit)));
|
||||
}
|
||||
|
||||
static forcedinline double JUCE_VECTOR_CALLTYPE sum (__m128d a) noexcept
|
||||
{
|
||||
#if defined(__SSE4__)
|
||||
__m128d retval = _mm_dp_pd (a, vconst (kOne), 0xff);
|
||||
#elif defined(__SSE3__)
|
||||
__m128d retval = _mm_hadd_pd (a, a);
|
||||
#else
|
||||
__m128d retval = _mm_add_pd (_mm_shuffle_pd (a, a, 0x01), a);
|
||||
#endif
|
||||
return _mm_cvtsd_f64 (retval);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 8-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int8_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (int8_t, kAllBitsSet);
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int8_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const int8_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, int8_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (int8_t s) noexcept { return _mm_set1_epi8 (s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
#if defined(__SSE4__)
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { return _mm_min_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { return _mm_max_epi8 (a, b); }
|
||||
#else
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { __m128i lt = greaterThan (b, a); return bit_or (bit_and (lt, a), bit_andnot (lt, b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { __m128i gt = greaterThan (a, b); return bit_or (bit_and (gt, a), bit_andnot (gt, b)); }
|
||||
#endif
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { return _mm_cmpeq_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { return _mm_cmpgt_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline int8_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<int8_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, int8_t s) noexcept { return SIMDFallbackOps<int8_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline int8_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept
|
||||
{
|
||||
#ifdef __SSSE3__
|
||||
__m128i lo = _mm_unpacklo_epi8 (a, _mm_setzero_si128());
|
||||
__m128i hi = _mm_unpackhi_epi8 (a, _mm_setzero_si128());
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
lo = _mm_hadd_epi16 (lo, lo);
|
||||
hi = _mm_hadd_epi16 (hi, hi);
|
||||
}
|
||||
|
||||
return static_cast<int8_t> ((_mm_cvtsi128_si32 (lo) & 0xff) + (_mm_cvtsi128_si32 (hi) & 0xff));
|
||||
#else
|
||||
return SIMDFallbackOps<int8_t, __m128i>::sum (a);
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b)
|
||||
{
|
||||
// unpack and multiply
|
||||
__m128i even = _mm_mullo_epi16 (a, b);
|
||||
__m128i odd = _mm_mullo_epi16 (_mm_srli_epi16 (a, 8), _mm_srli_epi16 (b, 8));
|
||||
|
||||
return _mm_or_si128 (_mm_slli_epi16 (odd, 8),
|
||||
_mm_srli_epi16 (_mm_slli_epi16 (even, 8), 8));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 8-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint8_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (uint8_t, kHighBit);
|
||||
DECLARE_SSE_SIMD_CONST (uint8_t, kAllBitsSet);
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const uint8_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const uint8_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, uint8_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (uint8_t s) noexcept { return _mm_set1_epi8 ((int8_t) s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { return _mm_min_epu8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { return _mm_max_epu8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { return _mm_cmpeq_epi8 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { return _mm_cmpgt_epi8 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline uint8_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<uint8_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, uint8_t s) noexcept { return SIMDFallbackOps<uint8_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline uint8_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept
|
||||
{
|
||||
#ifdef __SSSE3__
|
||||
__m128i lo = _mm_unpacklo_epi8 (a, _mm_setzero_si128());
|
||||
__m128i hi = _mm_unpackhi_epi8 (a, _mm_setzero_si128());
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
lo = _mm_hadd_epi16 (lo, lo);
|
||||
hi = _mm_hadd_epi16 (hi, hi);
|
||||
}
|
||||
|
||||
return static_cast<uint8_t> ((static_cast<uint32_t> (_mm_cvtsi128_si32 (lo)) & 0xffu)
|
||||
+ (static_cast<uint32_t> (_mm_cvtsi128_si32 (hi)) & 0xffu));
|
||||
#else
|
||||
return SIMDFallbackOps<uint8_t, __m128i>::sum (a);
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b)
|
||||
{
|
||||
// unpack and multiply
|
||||
__m128i even = _mm_mullo_epi16 (a, b);
|
||||
__m128i odd = _mm_mullo_epi16 (_mm_srli_epi16 (a, 8), _mm_srli_epi16 (b, 8));
|
||||
|
||||
return _mm_or_si128 (_mm_slli_epi16 (odd, 8),
|
||||
_mm_srli_epi16 (_mm_slli_epi16 (even, 8), 8));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 16-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int16_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (int16_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int16_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const int16_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, int16_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (int16_t s) noexcept { return _mm_set1_epi16 (s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept { return _mm_mullo_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { return _mm_min_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { return _mm_max_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { return _mm_cmpeq_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { return _mm_cmpgt_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline int16_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<int16_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, int16_t s) noexcept { return SIMDFallbackOps<int16_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline int16_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept
|
||||
{
|
||||
#ifdef __SSSE3__
|
||||
__m128i tmp = _mm_hadd_epi16 (a, a);
|
||||
tmp = _mm_hadd_epi16 (tmp, tmp);
|
||||
tmp = _mm_hadd_epi16 (tmp, tmp);
|
||||
|
||||
return static_cast<int16_t> (_mm_cvtsi128_si32 (tmp) & 0xffff);
|
||||
#else
|
||||
return SIMDFallbackOps<int16_t, __m128i>::sum (a);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 16-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint16_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (uint16_t, kHighBit);
|
||||
DECLARE_SSE_SIMD_CONST (uint16_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const uint16_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const uint16_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, uint16_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (uint16_t s) noexcept { return _mm_set1_epi16 ((int16_t) s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept { return _mm_mullo_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
#if defined(__SSE4__)
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { return _mm_min_epu16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { return _mm_max_epu16 (a, b); }
|
||||
#else
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { __m128i lt = greaterThan (b, a); return bit_or (bit_and (lt, a), bit_andnot (lt, b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { __m128i gt = greaterThan (a, b); return bit_or (bit_and (gt, a), bit_andnot (gt, b)); }
|
||||
#endif
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { return _mm_cmpeq_epi16 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { return _mm_cmpgt_epi16 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline uint16_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<uint16_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, uint16_t s) noexcept { return SIMDFallbackOps<uint16_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline uint16_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept
|
||||
{
|
||||
#ifdef __SSSE3__
|
||||
__m128i tmp = _mm_hadd_epi16 (a, a);
|
||||
tmp = _mm_hadd_epi16 (tmp, tmp);
|
||||
tmp = _mm_hadd_epi16 (tmp, tmp);
|
||||
|
||||
return static_cast<uint16_t> (static_cast<uint32_t> (_mm_cvtsi128_si32 (tmp)) & 0xffffu);
|
||||
#else
|
||||
return SIMDFallbackOps<uint16_t, __m128i>::sum (a);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 32-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int32_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (int32_t, kAllBitsSet);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int32_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const int32_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, int32_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (int32_t s) noexcept { return _mm_set1_epi32 (s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { return _mm_cmpeq_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { return _mm_cmpgt_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline int32_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<int32_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, int32_t s) noexcept { return SIMDFallbackOps<int32_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline int32_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept
|
||||
{
|
||||
#ifdef __SSSE3__
|
||||
__m128i tmp = _mm_hadd_epi32 (a, a);
|
||||
return _mm_cvtsi128_si32 (_mm_hadd_epi32 (tmp, tmp));
|
||||
#else
|
||||
return SIMDFallbackOps<int32_t, __m128i>::sum (a);
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_mullo_epi32 (a, b);
|
||||
#else
|
||||
__m128i even = _mm_mul_epu32 (a,b);
|
||||
__m128i odd = _mm_mul_epu32 (_mm_srli_si128 (a,4), _mm_srli_si128 (b,4));
|
||||
return _mm_unpacklo_epi32 (_mm_shuffle_epi32(even, _MM_SHUFFLE (0,0,2,0)),
|
||||
_mm_shuffle_epi32(odd, _MM_SHUFFLE (0,0,2,0)));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_min_epi32 (a, b);
|
||||
#else
|
||||
__m128i lt = greaterThan (b, a);
|
||||
return bit_or (bit_and (lt, a), bit_andnot (lt, b));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_max_epi32 (a, b);
|
||||
#else
|
||||
__m128i gt = greaterThan (a, b);
|
||||
return bit_or (bit_and (gt, a), bit_andnot (gt, b));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 32-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint32_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (uint32_t, kAllBitsSet);
|
||||
DECLARE_SSE_SIMD_CONST (uint32_t, kHighBit);
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const uint32_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const uint32_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, uint32_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (uint32_t s) noexcept { return _mm_set1_epi32 ((int32_t) s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { return _mm_cmpeq_epi32 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { return _mm_cmpgt_epi32 (ssign (a), ssign (b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline uint32_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<uint32_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, uint32_t s) noexcept { return SIMDFallbackOps<uint32_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
//==============================================================================
|
||||
static forcedinline uint32_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept
|
||||
{
|
||||
#ifdef __SSSE3__
|
||||
__m128i tmp = _mm_hadd_epi32 (a, a);
|
||||
return static_cast<uint32_t> (_mm_cvtsi128_si32 (_mm_hadd_epi32 (tmp, tmp)));
|
||||
#else
|
||||
return SIMDFallbackOps<uint32_t, __m128i>::sum (a);
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_mullo_epi32 (a, b);
|
||||
#else
|
||||
__m128i even = _mm_mul_epu32 (a,b);
|
||||
__m128i odd = _mm_mul_epu32 (_mm_srli_si128 (a,4), _mm_srli_si128 (b,4));
|
||||
return _mm_unpacklo_epi32 (_mm_shuffle_epi32(even, _MM_SHUFFLE (0,0,2,0)),
|
||||
_mm_shuffle_epi32(odd, _MM_SHUFFLE (0,0,2,0)));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_min_epi32 (a, b);
|
||||
#else
|
||||
__m128i lt = greaterThan (b, a);
|
||||
return bit_or (bit_and (lt, a), bit_andnot (lt, b));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_max_epi32 (a, b);
|
||||
#else
|
||||
__m128i gt = greaterThan (a, b);
|
||||
return bit_or (bit_and (gt, a), bit_andnot (gt, b));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Signed 64-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<int64_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (int64_t, kAllBitsSet);
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (int64_t s) noexcept { return _mm_set1_epi64x (s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const int64_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, int64_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi64 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi64 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { __m128i lt = greaterThan (b, a); return bit_or (bit_and (lt, a), bit_andnot (lt, b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { __m128i gt = greaterThan (a, b); return bit_or (bit_and (gt, a), bit_andnot (gt, b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline int64_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<int64_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, int64_t s) noexcept { return SIMDFallbackOps<int64_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline int64_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept { return SIMDFallbackOps<int64_t, __m128i>::sum (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept { return SIMDFallbackOps<int64_t, __m128i>::mul (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_cmpeq_epi64 (a, b);
|
||||
#else
|
||||
__m128i bitmask = _mm_cmpeq_epi32 (a, b);
|
||||
bitmask = _mm_and_si128 (bitmask, _mm_shuffle_epi32 (bitmask, _MM_SHUFFLE (2, 3, 0, 1)));
|
||||
return _mm_shuffle_epi32 (bitmask, _MM_SHUFFLE (2, 2, 0, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_2__)
|
||||
return _mm_cmpgt_epi64 (a, b);
|
||||
#else
|
||||
return SIMDFallbackOps<int64_t, __m128i>::greaterThan (a, b);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Unsigned 64-bit integer SSE intrinsics.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <>
|
||||
struct SIMDNativeOps<uint64_t>
|
||||
{
|
||||
//==============================================================================
|
||||
using vSIMDType = __m128i;
|
||||
|
||||
//==============================================================================
|
||||
DECLARE_SSE_SIMD_CONST (uint64_t, kAllBitsSet);
|
||||
DECLARE_SSE_SIMD_CONST (uint64_t, kHighBit);
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const uint64_t* a) noexcept { return load (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (uint64_t s) noexcept { return _mm_set1_epi64x ((int64_t) s); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE load (const uint64_t* a) noexcept { return _mm_load_si128 (reinterpret_cast<const __m128i*> (a)); }
|
||||
static forcedinline void JUCE_VECTOR_CALLTYPE store (__m128i v, uint64_t* p) noexcept { _mm_store_si128 (reinterpret_cast<__m128i*> (p), v); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE add (__m128i a, __m128i b) noexcept { return _mm_add_epi64 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE sub (__m128i a, __m128i b) noexcept { return _mm_sub_epi64 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_and (__m128i a, __m128i b) noexcept { return _mm_and_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_or (__m128i a, __m128i b) noexcept { return _mm_or_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { __m128i lt = greaterThan (b, a); return bit_or (bit_and (lt, a), bit_andnot (lt, b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { __m128i gt = greaterThan (a, b); return bit_or (bit_and (gt, a), bit_andnot (gt, b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThanOrEqual (__m128i a, __m128i b) noexcept { return bit_or (greaterThan (a, b), equal (a,b)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE multiplyAdd (__m128i a, __m128i b, __m128i c) noexcept { return add (a, mul (b, c)); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE notEqual (__m128i a, __m128i b) noexcept { return bit_not (equal (a, b)); }
|
||||
static forcedinline bool JUCE_VECTOR_CALLTYPE allEqual (__m128i a, __m128i b) noexcept { return (_mm_movemask_epi8 (equal (a, b)) == 0xffff); }
|
||||
static forcedinline uint64_t JUCE_VECTOR_CALLTYPE get (__m128i v, size_t i) noexcept { return SIMDFallbackOps<uint64_t, __m128i>::get (v, i); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE set (__m128i v, size_t i, uint64_t s) noexcept { return SIMDFallbackOps<uint64_t, __m128i>::set (v, i, s); }
|
||||
static forcedinline uint64_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept { return SIMDFallbackOps<uint64_t, __m128i>::sum (a); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept { return SIMDFallbackOps<uint64_t, __m128i>::mul (a, b); }
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE truncate (__m128i a) noexcept { return a; }
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
return _mm_cmpeq_epi64 (a, b);
|
||||
#else
|
||||
__m128i bitmask = _mm_cmpeq_epi32 (a, b);
|
||||
bitmask = _mm_and_si128 (bitmask, _mm_shuffle_epi32 (bitmask, _MM_SHUFFLE (2, 3, 0, 1)));
|
||||
return _mm_shuffle_epi32 (bitmask, _MM_SHUFFLE (2, 2, 0, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept
|
||||
{
|
||||
#if defined(__SSE4_2__)
|
||||
return _mm_cmpgt_epi64 (ssign (a), ssign (b));
|
||||
#else
|
||||
return SIMDFallbackOps<uint64_t, __m128i>::greaterThan (a, b);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
130
deps/juce/modules/juce_dsp/processors/juce_BallisticsFilter.cpp
vendored
Normal file
130
deps/juce/modules/juce_dsp/processors/juce_BallisticsFilter.cpp
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
BallisticsFilter<SampleType>::BallisticsFilter()
|
||||
{
|
||||
setAttackTime (attackTime);
|
||||
setReleaseTime (releaseTime);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::setAttackTime (SampleType attackTimeMs)
|
||||
{
|
||||
attackTime = attackTimeMs;
|
||||
cteAT = calculateLimitedCte (static_cast<SampleType> (attackTime));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::setReleaseTime (SampleType releaseTimeMs)
|
||||
{
|
||||
releaseTime = releaseTimeMs;
|
||||
cteRL = calculateLimitedCte (static_cast<SampleType> (releaseTime));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::setLevelCalculationType (LevelCalculationType newLevelType)
|
||||
{
|
||||
levelType = newLevelType;
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
expFactor = -2.0 * MathConstants<double>::pi * 1000.0 / sampleRate;
|
||||
|
||||
setAttackTime (attackTime);
|
||||
setReleaseTime (releaseTime);
|
||||
|
||||
yold.resize (spec.numChannels);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::reset()
|
||||
{
|
||||
reset (0);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::reset (SampleType initialValue)
|
||||
{
|
||||
for (auto& old : yold)
|
||||
old = initialValue;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
SampleType BallisticsFilter<SampleType>::processSample (int channel, SampleType inputValue)
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, yold.size()));
|
||||
|
||||
if (levelType == LevelCalculationType::RMS)
|
||||
inputValue *= inputValue;
|
||||
else
|
||||
inputValue = std::abs (inputValue);
|
||||
|
||||
SampleType cte = (inputValue > yold[(size_t) channel] ? cteAT : cteRL);
|
||||
|
||||
SampleType result = inputValue + cte * (yold[(size_t) channel] - inputValue);
|
||||
yold[(size_t) channel] = result;
|
||||
|
||||
if (levelType == LevelCalculationType::RMS)
|
||||
return std::sqrt (result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void BallisticsFilter<SampleType>::snapToZero() noexcept
|
||||
{
|
||||
for (auto& old : yold)
|
||||
util::snapToZero (old);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
SampleType BallisticsFilter<SampleType>::calculateLimitedCte (SampleType timeMs) const noexcept
|
||||
{
|
||||
return timeMs < static_cast<SampleType> (1.0e-3) ? 0
|
||||
: static_cast<SampleType> (std::exp (expFactor / timeMs));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class BallisticsFilter<float>;
|
||||
template class BallisticsFilter<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
150
deps/juce/modules/juce_dsp/processors/juce_BallisticsFilter.h
vendored
Normal file
150
deps/juce/modules/juce_dsp/processors/juce_BallisticsFilter.h
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class BallisticsFilterLevelCalculationType
|
||||
{
|
||||
peak,
|
||||
RMS
|
||||
};
|
||||
|
||||
/**
|
||||
A processor to apply standard attack / release ballistics to an input signal.
|
||||
This is useful in dynamics processors, envelope followers, modulated audio
|
||||
effects and for smoothing animation in data visualisation.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class BallisticsFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using LevelCalculationType = BallisticsFilterLevelCalculationType;
|
||||
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
BallisticsFilter();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the attack time in ms.
|
||||
|
||||
Attack times less than 0.001 ms will be snapped to zero and very long attack
|
||||
times will eventually saturate depending on the numerical precision used.
|
||||
*/
|
||||
void setAttackTime (SampleType attackTimeMs);
|
||||
|
||||
/** Sets the release time in ms.
|
||||
|
||||
Release times less than 0.001 ms will be snapped to zero and very long
|
||||
release times will eventually saturate depending on the numerical precision
|
||||
used.
|
||||
*/
|
||||
void setReleaseTime (SampleType releaseTimeMs);
|
||||
|
||||
/** Sets how the filter levels are calculated.
|
||||
|
||||
Level calculation in digital envelope followers is usually performed using
|
||||
peak detection with a rectifier function (like std::abs) and filtering,
|
||||
which returns an envelope dependant on the peak or maximum values of the
|
||||
signal amplitude.
|
||||
|
||||
To perform an estimation of the average value of the signal you can use
|
||||
an RMS (root mean squared) implementation of the ballistics filter instead.
|
||||
This is useful in some compressor and noise-gate designs, or in specific
|
||||
types of volume meters.
|
||||
*/
|
||||
void setLevelCalculationType (LevelCalculationType newCalculationType);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the filter. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the filter. */
|
||||
void reset();
|
||||
|
||||
/** Resets the internal state variables of the filter to the given initial value. */
|
||||
void reset (SampleType initialValue);
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() <= yold.size());
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
|
||||
}
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Processes one sample at a time on a given channel. */
|
||||
SampleType processSample (int channel, SampleType inputValue);
|
||||
|
||||
/** Ensure that the state variables are rounded to zero if the state
|
||||
variables are denormals. This is only needed if you are doing
|
||||
sample by sample processing.
|
||||
*/
|
||||
void snapToZero() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SampleType calculateLimitedCte (SampleType) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
std::vector<SampleType> yold;
|
||||
double sampleRate = 44100.0, expFactor = -0.142;
|
||||
SampleType attackTime = 1.0, releaseTime = 100.0, cteAT = 0.0, cteRL = 0.0;
|
||||
LevelCalculationType levelType = LevelCalculationType::peak;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
138
deps/juce/modules/juce_dsp/processors/juce_DelayLine.cpp
vendored
Normal file
138
deps/juce/modules/juce_dsp/processors/juce_DelayLine.cpp
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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, typename InterpolationType>
|
||||
DelayLine<SampleType, InterpolationType>::DelayLine()
|
||||
: DelayLine (0)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
DelayLine<SampleType, InterpolationType>::DelayLine (int maximumDelayInSamples)
|
||||
{
|
||||
jassert (maximumDelayInSamples >= 0);
|
||||
|
||||
sampleRate = 44100.0;
|
||||
|
||||
setMaximumDelayInSamples (maximumDelayInSamples);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
void DelayLine<SampleType, InterpolationType>::setDelay (SampleType newDelayInSamples)
|
||||
{
|
||||
auto upperLimit = (SampleType) getMaximumDelayInSamples();
|
||||
jassert (isPositiveAndNotGreaterThan (newDelayInSamples, upperLimit));
|
||||
|
||||
delay = jlimit ((SampleType) 0, upperLimit, newDelayInSamples);
|
||||
delayInt = static_cast<int> (std::floor (delay));
|
||||
delayFrac = delay - (SampleType) delayInt;
|
||||
|
||||
updateInternalVariables();
|
||||
}
|
||||
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
SampleType DelayLine<SampleType, InterpolationType>::getDelay() const
|
||||
{
|
||||
return delay;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
void DelayLine<SampleType, InterpolationType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
bufferData.setSize ((int) spec.numChannels, totalSize, false, false, true);
|
||||
|
||||
writePos.resize (spec.numChannels);
|
||||
readPos.resize (spec.numChannels);
|
||||
|
||||
v.resize (spec.numChannels);
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
void DelayLine<SampleType, InterpolationType>::setMaximumDelayInSamples (int maxDelayInSamples)
|
||||
{
|
||||
jassert (maxDelayInSamples >= 0);
|
||||
totalSize = jmax (4, maxDelayInSamples + 1);
|
||||
bufferData.setSize ((int) bufferData.getNumChannels(), totalSize, false, false, true);
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
void DelayLine<SampleType, InterpolationType>::reset()
|
||||
{
|
||||
for (auto vec : { &writePos, &readPos })
|
||||
std::fill (vec->begin(), vec->end(), 0);
|
||||
|
||||
std::fill (v.begin(), v.end(), static_cast<SampleType> (0));
|
||||
|
||||
bufferData.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
void DelayLine<SampleType, InterpolationType>::pushSample (int channel, SampleType sample)
|
||||
{
|
||||
bufferData.setSample (channel, writePos[(size_t) channel], sample);
|
||||
writePos[(size_t) channel] = (writePos[(size_t) channel] + totalSize - 1) % totalSize;
|
||||
}
|
||||
|
||||
template <typename SampleType, typename InterpolationType>
|
||||
SampleType DelayLine<SampleType, InterpolationType>::popSample (int channel, SampleType delayInSamples, bool updateReadPointer)
|
||||
{
|
||||
if (delayInSamples >= 0)
|
||||
setDelay(delayInSamples);
|
||||
|
||||
auto result = interpolateSample (channel);
|
||||
|
||||
if (updateReadPointer)
|
||||
readPos[(size_t) channel] = (readPos[(size_t) channel] + totalSize - 1) % totalSize;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class DelayLine<float, DelayLineInterpolationTypes::None>;
|
||||
template class DelayLine<double, DelayLineInterpolationTypes::None>;
|
||||
template class DelayLine<float, DelayLineInterpolationTypes::Linear>;
|
||||
template class DelayLine<double, DelayLineInterpolationTypes::Linear>;
|
||||
template class DelayLine<float, DelayLineInterpolationTypes::Lagrange3rd>;
|
||||
template class DelayLine<double, DelayLineInterpolationTypes::Lagrange3rd>;
|
||||
template class DelayLine<float, DelayLineInterpolationTypes::Thiran>;
|
||||
template class DelayLine<double, DelayLineInterpolationTypes::Thiran>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
339
deps/juce/modules/juce_dsp/processors/juce_DelayLine.h
vendored
Normal file
339
deps/juce/modules/juce_dsp/processors/juce_DelayLine.h
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A collection of structs to pass as the template argument when setting the
|
||||
interpolation type for the DelayLine class.
|
||||
*/
|
||||
namespace DelayLineInterpolationTypes
|
||||
{
|
||||
/**
|
||||
No interpolation between successive samples in the delay line will be
|
||||
performed. This is useful when the delay is a constant integer or to
|
||||
create lo-fi audio effects.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct None {};
|
||||
|
||||
/**
|
||||
Successive samples in the delay line will be linearly interpolated. This
|
||||
type of interpolation has a low compuational cost where the delay can be
|
||||
modulated in real time, but it also introduces a low-pass filtering effect
|
||||
into your audio signal.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct Linear {};
|
||||
|
||||
/**
|
||||
Successive samples in the delay line will be interpolated using a 3rd order
|
||||
Lagrange interpolator. This method incurs more computational overhead than
|
||||
linear interpolation but reduces the low-pass filtering effect whilst
|
||||
remaining amenable to real time delay modulation.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct Lagrange3rd {};
|
||||
|
||||
/**
|
||||
Successive samples in the delay line will be interpolated using 1st order
|
||||
Thiran interpolation. This method is very efficient, and features a flat
|
||||
amplitude frequency response in exchange for less accuracy in the phase
|
||||
response. This interpolation method is stateful so is unsuitable for
|
||||
applications requiring fast delay modulation.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct Thiran {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A delay line processor featuring several algorithms for the fractional delay
|
||||
calculation, block processing, and sample-by-sample processing useful when
|
||||
modulating the delay in real time or creating a standard delay effect with
|
||||
feedback.
|
||||
|
||||
Note: If you intend to change the delay in real time, you may want to smooth
|
||||
changes to the delay systematically using either a ramp or a low-pass filter.
|
||||
|
||||
@see SmoothedValue, FirstOrderTPTFilter
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType, typename InterpolationType = DelayLineInterpolationTypes::Linear>
|
||||
class DelayLine
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Default constructor. */
|
||||
DelayLine();
|
||||
|
||||
/** Constructor. */
|
||||
explicit DelayLine (int maximumDelayInSamples);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the delay in samples. */
|
||||
void setDelay (SampleType newDelayInSamples);
|
||||
|
||||
/** Returns the current delay in samples. */
|
||||
SampleType getDelay() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Sets a new maximum delay in samples.
|
||||
|
||||
Also clears the delay line.
|
||||
|
||||
This may allocate internally, so you should never call it from the audio thread.
|
||||
*/
|
||||
void setMaximumDelayInSamples (int maxDelayInSamples);
|
||||
|
||||
/** Gets the maximum possible delay in samples.
|
||||
|
||||
For very short delay times, the result of getMaximumDelayInSamples() may
|
||||
differ from the last value passed to setMaximumDelayInSamples().
|
||||
*/
|
||||
int getMaximumDelayInSamples() const noexcept { return totalSize - 1; }
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Pushes a single sample into one channel of the delay line.
|
||||
|
||||
Use this function and popSample instead of process if you need to modulate
|
||||
the delay in real time instead of using a fixed delay value, or if you want
|
||||
to code a delay effect with a feedback loop.
|
||||
|
||||
@see setDelay, popSample, process
|
||||
*/
|
||||
void pushSample (int channel, SampleType sample);
|
||||
|
||||
/** Pops a single sample from one channel of the delay line.
|
||||
|
||||
Use this function to modulate the delay in real time or implement standard
|
||||
delay effects with feedback.
|
||||
|
||||
@param channel the target channel for the delay line.
|
||||
|
||||
@param delayInSamples sets the wanted fractional delay in samples, or -1
|
||||
to use the value being used before or set with
|
||||
setDelay function.
|
||||
|
||||
@param updateReadPointer should be set to true if you use the function
|
||||
once for each sample, or false if you need
|
||||
multi-tap delay capabilities.
|
||||
|
||||
@see setDelay, pushSample, process
|
||||
*/
|
||||
SampleType popSample (int channel, SampleType delayInSamples = -1, bool updateReadPointer = true);
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context.
|
||||
|
||||
Can be used for block processing when the delay is not going to change
|
||||
during processing. The delay must first be set by calling setDelay.
|
||||
|
||||
@see setDelay
|
||||
*/
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumChannels() == writePos.size());
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock.getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
pushSample ((int) channel, inputSamples[i]);
|
||||
outputSamples[i] = popSample ((int) channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::None>::value, SampleType>::type
|
||||
interpolateSample (int channel) const
|
||||
{
|
||||
auto index = (readPos[(size_t) channel] + delayInt) % totalSize;
|
||||
return bufferData.getSample (channel, index);
|
||||
}
|
||||
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::Linear>::value, SampleType>::type
|
||||
interpolateSample (int channel) const
|
||||
{
|
||||
auto index1 = readPos[(size_t) channel] + delayInt;
|
||||
auto index2 = index1 + 1;
|
||||
|
||||
if (index2 >= totalSize)
|
||||
{
|
||||
index1 %= totalSize;
|
||||
index2 %= totalSize;
|
||||
}
|
||||
|
||||
auto value1 = bufferData.getSample (channel, index1);
|
||||
auto value2 = bufferData.getSample (channel, index2);
|
||||
|
||||
return value1 + delayFrac * (value2 - value1);
|
||||
}
|
||||
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::Lagrange3rd>::value, SampleType>::type
|
||||
interpolateSample (int channel) const
|
||||
{
|
||||
auto index1 = readPos[(size_t) channel] + delayInt;
|
||||
auto index2 = index1 + 1;
|
||||
auto index3 = index2 + 1;
|
||||
auto index4 = index3 + 1;
|
||||
|
||||
if (index4 >= totalSize)
|
||||
{
|
||||
index1 %= totalSize;
|
||||
index2 %= totalSize;
|
||||
index3 %= totalSize;
|
||||
index4 %= totalSize;
|
||||
}
|
||||
|
||||
auto* samples = bufferData.getReadPointer (channel);
|
||||
|
||||
auto value1 = samples[index1];
|
||||
auto value2 = samples[index2];
|
||||
auto value3 = samples[index3];
|
||||
auto value4 = samples[index4];
|
||||
|
||||
auto d1 = delayFrac - 1.f;
|
||||
auto d2 = delayFrac - 2.f;
|
||||
auto d3 = delayFrac - 3.f;
|
||||
|
||||
auto c1 = -d1 * d2 * d3 / 6.f;
|
||||
auto c2 = d2 * d3 * 0.5f;
|
||||
auto c3 = -d1 * d3 * 0.5f;
|
||||
auto c4 = d1 * d2 / 6.f;
|
||||
|
||||
return value1 * c1 + delayFrac * (value2 * c2 + value3 * c3 + value4 * c4);
|
||||
}
|
||||
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::Thiran>::value, SampleType>::type
|
||||
interpolateSample (int channel)
|
||||
{
|
||||
auto index1 = readPos[(size_t) channel] + delayInt;
|
||||
auto index2 = index1 + 1;
|
||||
|
||||
if (index2 >= totalSize)
|
||||
{
|
||||
index1 %= totalSize;
|
||||
index2 %= totalSize;
|
||||
}
|
||||
|
||||
auto value1 = bufferData.getSample (channel, index1);
|
||||
auto value2 = bufferData.getSample (channel, index2);
|
||||
|
||||
auto output = delayFrac == 0 ? value1 : value2 + alpha * (value1 - v[(size_t) channel]);
|
||||
v[(size_t) channel] = output;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::None>::value, void>::type
|
||||
updateInternalVariables()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::Linear>::value, void>::type
|
||||
updateInternalVariables()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::Lagrange3rd>::value, void>::type
|
||||
updateInternalVariables()
|
||||
{
|
||||
if (delayInt >= 1)
|
||||
{
|
||||
delayFrac++;
|
||||
delayInt--;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = InterpolationType>
|
||||
typename std::enable_if <std::is_same <T, DelayLineInterpolationTypes::Thiran>::value, void>::type
|
||||
updateInternalVariables()
|
||||
{
|
||||
if (delayFrac < (SampleType) 0.618 && delayInt >= 1)
|
||||
{
|
||||
delayFrac++;
|
||||
delayInt--;
|
||||
}
|
||||
|
||||
alpha = (1 - delayFrac) / (1 + delayFrac);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
double sampleRate;
|
||||
|
||||
//==============================================================================
|
||||
AudioBuffer<SampleType> bufferData;
|
||||
std::vector<SampleType> v;
|
||||
std::vector<int> writePos, readPos;
|
||||
SampleType delay = 0.0, delayFrac = 0.0;
|
||||
int delayInt = 0, totalSize = 4;
|
||||
SampleType alpha = 0.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
371
deps/juce/modules/juce_dsp/processors/juce_DryWetMixer.cpp
vendored
Normal file
371
deps/juce/modules/juce_dsp/processors/juce_DryWetMixer.cpp
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
DryWetMixer<SampleType>::DryWetMixer()
|
||||
: DryWetMixer (0)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
DryWetMixer<SampleType>::DryWetMixer (int maximumWetLatencyInSamplesIn)
|
||||
: dryDelayLine (maximumWetLatencyInSamplesIn),
|
||||
maximumWetLatencyInSamples (maximumWetLatencyInSamplesIn)
|
||||
{
|
||||
dryDelayLine.setDelay (0);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::setMixingRule (MixingRule newRule)
|
||||
{
|
||||
currentMixingRule = newRule;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::setWetMixProportion (SampleType newWetMixProportion)
|
||||
{
|
||||
jassert (isPositiveAndNotGreaterThan (newWetMixProportion, 1.0));
|
||||
|
||||
mix = jlimit (static_cast<SampleType> (0.0), static_cast<SampleType> (1.0), newWetMixProportion);
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::setWetLatency (SampleType wetLatencySamples)
|
||||
{
|
||||
dryDelayLine.setDelay (wetLatencySamples);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
dryDelayLine.prepare (spec);
|
||||
bufferDry.setSize ((int) spec.numChannels, (int) spec.maximumBlockSize, false, false, true);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::reset()
|
||||
{
|
||||
dryVolume.reset (sampleRate, 0.05);
|
||||
wetVolume.reset (sampleRate, 0.05);
|
||||
|
||||
dryDelayLine.reset();
|
||||
|
||||
fifo = SingleThreadedAbstractFifo (nextPowerOfTwo (bufferDry.getNumSamples()));
|
||||
bufferDry.setSize (bufferDry.getNumChannels(), fifo.getSize(), false, false, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::pushDrySamples (const AudioBlock<const SampleType> drySamples)
|
||||
{
|
||||
jassert (drySamples.getNumChannels() <= (size_t) bufferDry.getNumChannels());
|
||||
jassert (drySamples.getNumSamples() <= (size_t) fifo.getRemainingSpace());
|
||||
|
||||
auto offset = 0;
|
||||
|
||||
for (const auto& range : fifo.write ((int) drySamples.getNumSamples()))
|
||||
{
|
||||
if (range.getLength() == 0)
|
||||
continue;
|
||||
|
||||
auto block = AudioBlock<SampleType> (bufferDry).getSubsetChannelBlock (0, drySamples.getNumChannels())
|
||||
.getSubBlock ((size_t) range.getStart(), (size_t) range.getLength());
|
||||
|
||||
auto inputBlock = drySamples.getSubBlock ((size_t) offset, (size_t) range.getLength());
|
||||
|
||||
if (maximumWetLatencyInSamples == 0)
|
||||
block.copyFrom (inputBlock);
|
||||
else
|
||||
dryDelayLine.process (ProcessContextNonReplacing<SampleType> (inputBlock, block));
|
||||
|
||||
offset += range.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::mixWetSamples (AudioBlock<SampleType> inOutBlock)
|
||||
{
|
||||
inOutBlock.multiplyBy (wetVolume);
|
||||
|
||||
jassert (inOutBlock.getNumSamples() <= (size_t) fifo.getNumReadable());
|
||||
|
||||
auto offset = 0;
|
||||
|
||||
for (const auto& range : fifo.read ((int) inOutBlock.getNumSamples()))
|
||||
{
|
||||
if (range.getLength() == 0)
|
||||
continue;
|
||||
|
||||
auto block = AudioBlock<SampleType> (bufferDry).getSubsetChannelBlock (0, inOutBlock.getNumChannels())
|
||||
.getSubBlock ((size_t) range.getStart(), (size_t) range.getLength());
|
||||
block.multiplyBy (dryVolume);
|
||||
inOutBlock.getSubBlock ((size_t) offset).add (block);
|
||||
|
||||
offset += range.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void DryWetMixer<SampleType>::update()
|
||||
{
|
||||
SampleType dryValue, wetValue;
|
||||
|
||||
switch (currentMixingRule)
|
||||
{
|
||||
case MixingRule::balanced:
|
||||
dryValue = static_cast<SampleType> (2.0) * jmin (static_cast<SampleType> (0.5), static_cast<SampleType> (1.0) - mix);
|
||||
wetValue = static_cast<SampleType> (2.0) * jmin (static_cast<SampleType> (0.5), mix);
|
||||
break;
|
||||
|
||||
case MixingRule::linear:
|
||||
dryValue = static_cast<SampleType> (1.0) - mix;
|
||||
wetValue = mix;
|
||||
break;
|
||||
|
||||
case MixingRule::sin3dB:
|
||||
dryValue = static_cast<SampleType> (std::sin (0.5 * MathConstants<double>::pi * (1.0 - mix)));
|
||||
wetValue = static_cast<SampleType> (std::sin (0.5 * MathConstants<double>::pi * mix));
|
||||
break;
|
||||
|
||||
case MixingRule::sin4p5dB:
|
||||
dryValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * (1.0 - mix)), 1.5));
|
||||
wetValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * mix), 1.5));
|
||||
break;
|
||||
|
||||
case MixingRule::sin6dB:
|
||||
dryValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * (1.0 - mix)), 2.0));
|
||||
wetValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * mix), 2.0));
|
||||
break;
|
||||
|
||||
case MixingRule::squareRoot3dB:
|
||||
dryValue = std::sqrt (static_cast<SampleType> (1.0) - mix);
|
||||
wetValue = std::sqrt (mix);
|
||||
break;
|
||||
|
||||
case MixingRule::squareRoot4p5dB:
|
||||
dryValue = static_cast<SampleType> (std::pow (std::sqrt (1.0 - mix), 1.5));
|
||||
wetValue = static_cast<SampleType> (std::pow (std::sqrt (mix), 1.5));
|
||||
break;
|
||||
|
||||
default:
|
||||
dryValue = jmin (static_cast<SampleType> (0.5), static_cast<SampleType> (1.0) - mix);
|
||||
wetValue = jmin (static_cast<SampleType> (0.5), mix);
|
||||
break;
|
||||
}
|
||||
|
||||
dryVolume.setTargetValue (dryValue);
|
||||
wetVolume.setTargetValue (wetValue);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class DryWetMixer<float>;
|
||||
template class DryWetMixer<double>;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
struct DryWetMixerTests : public UnitTest
|
||||
{
|
||||
DryWetMixerTests() : UnitTest ("DryWetMixer", UnitTestCategories::dsp) {}
|
||||
|
||||
enum class Kind { down, up };
|
||||
|
||||
static auto getRampBuffer (ProcessSpec spec, Kind kind)
|
||||
{
|
||||
AudioBuffer<float> buffer ((int) spec.numChannels, (int) spec.maximumBlockSize);
|
||||
|
||||
for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
|
||||
{
|
||||
for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
|
||||
{
|
||||
const auto ramp = kind == Kind::up ? sample : spec.maximumBlockSize - sample;
|
||||
|
||||
buffer.setSample ((int) channel,
|
||||
(int) sample,
|
||||
jmap ((float) ramp, 0.0f, (float) spec.maximumBlockSize, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
constexpr ProcessSpec spec { 44100.0, 512, 2 };
|
||||
constexpr auto numBlocks = 5;
|
||||
|
||||
const auto wetBuffer = getRampBuffer (spec, Kind::up);
|
||||
const auto dryBuffer = getRampBuffer (spec, Kind::down);
|
||||
|
||||
for (auto maxLatency : { 0, 100, 200, 512 })
|
||||
{
|
||||
beginTest ("Mixer can push multiple small buffers");
|
||||
{
|
||||
DryWetMixer<float> mixer (maxLatency);
|
||||
mixer.setWetMixProportion (0.5f);
|
||||
mixer.prepare (spec);
|
||||
|
||||
for (auto block = 0; block < numBlocks; ++block)
|
||||
{
|
||||
// Push samples one-by-one
|
||||
for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
|
||||
mixer.pushDrySamples (AudioBlock<const float> (dryBuffer).getSubBlock (sample, 1));
|
||||
|
||||
// Mix wet samples in one go
|
||||
auto outputBlock = wetBuffer;
|
||||
mixer.mixWetSamples ({ outputBlock });
|
||||
|
||||
// The output block should contain the wet and dry samples averaged
|
||||
for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
|
||||
{
|
||||
for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
|
||||
{
|
||||
const auto outputValue = outputBlock.getSample ((int) channel, (int) sample);
|
||||
expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Mixer can pop multiple small buffers");
|
||||
{
|
||||
DryWetMixer<float> mixer (maxLatency);
|
||||
mixer.setWetMixProportion (0.5f);
|
||||
mixer.prepare (spec);
|
||||
|
||||
for (auto block = 0; block < numBlocks; ++block)
|
||||
{
|
||||
// Push samples in one go
|
||||
mixer.pushDrySamples ({ dryBuffer });
|
||||
|
||||
// Process wet samples one-by-one
|
||||
for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
|
||||
{
|
||||
AudioBuffer<float> outputBlock ((int) spec.numChannels, 1);
|
||||
AudioBlock<const float> (wetBuffer).getSubBlock (sample, 1).copyTo (outputBlock);
|
||||
mixer.mixWetSamples ({ outputBlock });
|
||||
|
||||
// The output block should contain the wet and dry samples averaged
|
||||
for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
|
||||
{
|
||||
const auto outputValue = outputBlock.getSample ((int) channel, 0);
|
||||
expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Mixer can push and pop multiple small buffers");
|
||||
{
|
||||
DryWetMixer<float> mixer (maxLatency);
|
||||
mixer.setWetMixProportion (0.5f);
|
||||
mixer.prepare (spec);
|
||||
|
||||
for (auto block = 0; block < numBlocks; ++block)
|
||||
{
|
||||
// Push dry samples and process wet samples one-by-one
|
||||
for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
|
||||
{
|
||||
mixer.pushDrySamples (AudioBlock<const float> (dryBuffer).getSubBlock (sample, 1));
|
||||
|
||||
AudioBuffer<float> outputBlock ((int) spec.numChannels, 1);
|
||||
AudioBlock<const float> (wetBuffer).getSubBlock (sample, 1).copyTo (outputBlock);
|
||||
mixer.mixWetSamples ({ outputBlock });
|
||||
|
||||
// The output block should contain the wet and dry samples averaged
|
||||
for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
|
||||
{
|
||||
const auto outputValue = outputBlock.getSample ((int) channel, 0);
|
||||
expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Mixer can push and pop full-sized blocks after encountering a shorter block");
|
||||
{
|
||||
DryWetMixer<float> mixer (maxLatency);
|
||||
mixer.setWetMixProportion (0.5f);
|
||||
mixer.prepare (spec);
|
||||
|
||||
constexpr auto shortBlockLength = spec.maximumBlockSize / 2;
|
||||
AudioBuffer<float> shortBlock (spec.numChannels, shortBlockLength);
|
||||
mixer.pushDrySamples (AudioBlock<const float> (dryBuffer).getSubBlock (shortBlockLength));
|
||||
mixer.mixWetSamples ({ shortBlock });
|
||||
|
||||
for (auto block = 0; block < numBlocks; ++block)
|
||||
{
|
||||
// Push a full block of dry samples
|
||||
mixer.pushDrySamples ({ dryBuffer });
|
||||
|
||||
// Mix a full block of wet samples
|
||||
auto outputBlock = wetBuffer;
|
||||
mixer.mixWetSamples ({ outputBlock });
|
||||
|
||||
// The output block should contain the wet and dry samples averaged
|
||||
for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
|
||||
{
|
||||
for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
|
||||
{
|
||||
const auto outputValue = outputBlock.getSample ((int) channel, (int) sample);
|
||||
expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const DryWetMixerTests dryWetMixerTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
120
deps/juce/modules/juce_dsp/processors/juce_DryWetMixer.h
vendored
Normal file
120
deps/juce/modules/juce_dsp/processors/juce_DryWetMixer.h
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class DryWetMixingRule
|
||||
{
|
||||
linear, // dry volume is equal to 1 - wet volume
|
||||
balanced, // both dry and wet are 1 when mix is 0.5, with dry decreasing to 0
|
||||
// above this value and wet decreasing to 0 below it
|
||||
sin3dB, // alternate dry/wet mixing rule using the 3 dB sine panning rule
|
||||
sin4p5dB, // alternate dry/wet mixing rule using the 4.5 dB sine panning rule
|
||||
sin6dB, // alternate dry/wet mixing rule using the 6 dB sine panning rule
|
||||
squareRoot3dB, // alternate dry/wet mixing rule using the regular 3 dB panning rule
|
||||
squareRoot4p5dB // alternate dry/wet mixing rule using the regular 4.5 dB panning rule
|
||||
};
|
||||
|
||||
/**
|
||||
A processor to handle dry/wet mixing of two audio signals, where the wet signal
|
||||
may have additional latency.
|
||||
|
||||
Once a DryWetMixer object is configured, push the dry samples using pushDrySamples
|
||||
and mix into the fully wet samples using mixWetSamples.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class DryWetMixer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using MixingRule = DryWetMixingRule;
|
||||
|
||||
//==============================================================================
|
||||
/** Default constructor. */
|
||||
DryWetMixer();
|
||||
|
||||
/** Constructor. */
|
||||
explicit DryWetMixer (int maximumWetLatencyInSamples);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the mix rule. */
|
||||
void setMixingRule (MixingRule newRule);
|
||||
|
||||
/** Sets the current dry/wet mix proportion, with 0.0 being full dry and 1.0
|
||||
being fully wet.
|
||||
*/
|
||||
void setWetMixProportion (SampleType newWetMixProportion);
|
||||
|
||||
/** Sets the relative latency of the wet signal path compared to the dry signal
|
||||
path, and thus the amount of latency compensation that will be added to the
|
||||
dry samples in this processor.
|
||||
*/
|
||||
void setWetLatency (SampleType wetLatencyInSamples);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Copies the dry path samples into an internal delay line. */
|
||||
void pushDrySamples (const AudioBlock<const SampleType> drySamples);
|
||||
|
||||
/** Mixes the supplied wet samples with the latency-compensated dry samples from
|
||||
pushDrySamples.
|
||||
|
||||
@param wetSamples Input: The AudioBlock references fully wet samples.
|
||||
Output: The AudioBlock references the wet samples mixed
|
||||
with the latency compensated dry samples.
|
||||
|
||||
@see pushDrySamples
|
||||
*/
|
||||
void mixWetSamples (AudioBlock<SampleType> wetSamples);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> dryVolume, wetVolume;
|
||||
DelayLine<SampleType, DelayLineInterpolationTypes::Thiran> dryDelayLine;
|
||||
AudioBuffer<SampleType> bufferDry;
|
||||
|
||||
SingleThreadedAbstractFifo fifo;
|
||||
SampleType mix = 1.0;
|
||||
MixingRule currentMixingRule = MixingRule::linear;
|
||||
double sampleRate = 44100.0;
|
||||
int maximumWetLatencyInSamples = 0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
161
deps/juce/modules/juce_dsp/processors/juce_FIRFilter.cpp
vendored
Normal file
161
deps/juce/modules/juce_dsp/processors/juce_FIRFilter.cpp
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 NumericType>
|
||||
double FIR::Coefficients<NumericType>::Coefficients::getMagnitudeForFrequency (double frequency, double theSampleRate) const noexcept
|
||||
{
|
||||
jassert (theSampleRate > 0.0);
|
||||
jassert (frequency >= 0.0 && frequency <= theSampleRate * 0.5);
|
||||
|
||||
constexpr Complex<double> j (0, 1);
|
||||
auto order = getFilterOrder();
|
||||
|
||||
Complex<double> numerator = 0.0, factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequency * j / theSampleRate);
|
||||
|
||||
const auto* coefs = coefficients.begin();
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
return std::abs (numerator);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename NumericType>
|
||||
void FIR::Coefficients<NumericType>::Coefficients::getMagnitudeForFrequencyArray (double* frequencies, double* magnitudes,
|
||||
size_t numSamples, double theSampleRate) const noexcept
|
||||
{
|
||||
jassert (theSampleRate > 0.0);
|
||||
|
||||
constexpr Complex<double> j (0, 1);
|
||||
const auto* coefs = coefficients.begin();
|
||||
auto order = getFilterOrder();
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
jassert (frequencies[i] >= 0.0 && frequencies[i] <= theSampleRate * 0.5);
|
||||
|
||||
Complex<double> numerator = 0.0;
|
||||
Complex<double> factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequencies[i] * j / theSampleRate);
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
magnitudes[i] = std::abs (numerator);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename NumericType>
|
||||
double FIR::Coefficients<NumericType>::Coefficients::getPhaseForFrequency (double frequency, double theSampleRate) const noexcept
|
||||
{
|
||||
jassert (theSampleRate > 0.0);
|
||||
jassert (frequency >= 0.0 && frequency <= theSampleRate * 0.5);
|
||||
|
||||
constexpr Complex<double> j (0, 1);
|
||||
|
||||
Complex<double> numerator = 0.0;
|
||||
Complex<double> factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequency * j / theSampleRate);
|
||||
|
||||
const auto* coefs = coefficients.begin();
|
||||
auto order = getFilterOrder();
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
return std::arg (numerator);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename NumericType>
|
||||
void FIR::Coefficients<NumericType>::Coefficients::getPhaseForFrequencyArray (double* frequencies, double* phases,
|
||||
size_t numSamples, double theSampleRate) const noexcept
|
||||
{
|
||||
jassert (theSampleRate > 0.0);
|
||||
|
||||
constexpr Complex<double> j (0, 1);
|
||||
const auto* coefs = coefficients.begin();
|
||||
auto order = getFilterOrder();
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
jassert (frequencies[i] >= 0.0 && frequencies[i] <= theSampleRate * 0.5);
|
||||
|
||||
Complex<double> numerator = 0.0, factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequencies[i] * j / theSampleRate);
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
phases[i] = std::arg (numerator);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename NumericType>
|
||||
void FIR::Coefficients<NumericType>::Coefficients::normalise() noexcept
|
||||
{
|
||||
auto magnitude = static_cast<NumericType> (0);
|
||||
|
||||
auto* coefs = coefficients.getRawDataPointer();
|
||||
auto n = static_cast<size_t> (coefficients.size());
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
auto c = coefs[i];
|
||||
magnitude += c * c;
|
||||
}
|
||||
|
||||
auto magnitudeInv = 1 / (4 * std::sqrt (magnitude));
|
||||
|
||||
FloatVectorOperations::multiply (coefs, magnitudeInv, static_cast<int> (n));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template struct FIR::Coefficients<float>;
|
||||
template struct FIR::Coefficients<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
285
deps/juce/modules/juce_dsp/processors/juce_FIRFilter.h
vendored
Normal file
285
deps/juce/modules/juce_dsp/processors/juce_FIRFilter.h
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Classes for FIR filter processing.
|
||||
*/
|
||||
namespace FIR
|
||||
{
|
||||
template <typename NumericType>
|
||||
struct Coefficients;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A processing class that can perform FIR filtering on an audio signal, in the
|
||||
time domain.
|
||||
|
||||
Using FIRFilter is fast enough for FIRCoefficients with a size lower than 128
|
||||
samples. For longer filters, it might be more efficient to use the class
|
||||
Convolution instead, which does the same processing in the frequency domain
|
||||
thanks to FFT.
|
||||
|
||||
@see FIRFilter::Coefficients, Convolution, FFT
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
/** The NumericType is the underlying primitive type used by the SampleType (which
|
||||
could be either a primitive or vector)
|
||||
*/
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
/** A typedef for a ref-counted pointer to the coefficients object */
|
||||
using CoefficientsPtr = typename Coefficients<NumericType>::Ptr;
|
||||
|
||||
//==============================================================================
|
||||
/** This will create a filter which will produce silence. */
|
||||
Filter() : coefficients (new Coefficients<NumericType>) { reset(); }
|
||||
|
||||
/** Creates a filter with a given set of coefficients. */
|
||||
Filter (CoefficientsPtr coefficientsToUse) : coefficients (std::move (coefficientsToUse)) { reset(); }
|
||||
|
||||
Filter (const Filter&) = default;
|
||||
Filter (Filter&&) = default;
|
||||
Filter& operator= (const Filter&) = default;
|
||||
Filter& operator= (Filter&&) = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Prepare this filter for processing. */
|
||||
inline void prepare (const ProcessSpec& spec) noexcept
|
||||
{
|
||||
// This class can only process mono signals. Use the ProcessorDuplicator class
|
||||
// to apply this filter on a multi-channel audio stream.
|
||||
jassertquiet (spec.numChannels == 1);
|
||||
reset();
|
||||
}
|
||||
|
||||
/** Resets the filter's processing pipeline, ready to start a new stream of data.
|
||||
|
||||
Note that this clears the processing state, but the type of filter and
|
||||
its coefficients aren't changed. To disable the filter, call setEnabled (false).
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
if (coefficients != nullptr)
|
||||
{
|
||||
auto newSize = coefficients->getFilterOrder() + 1;
|
||||
|
||||
if (newSize != size)
|
||||
{
|
||||
memory.malloc (1 + jmax (newSize, size, static_cast<size_t> (128)));
|
||||
|
||||
fifo = snapPointerToAlignment (memory.getData(), sizeof (SampleType));
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
fifo[i] = SampleType {0};
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** The coefficients of the FIR filter. It's up to the caller to ensure that
|
||||
these coefficients are modified in a thread-safe way.
|
||||
|
||||
If you change the order of the coefficients then you must call reset after
|
||||
modifying them.
|
||||
*/
|
||||
typename Coefficients<NumericType>::Ptr coefficients;
|
||||
|
||||
//==============================================================================
|
||||
/** Processes a block of samples */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
static_assert (std::is_same<typename ProcessContext::SampleType, SampleType>::value,
|
||||
"The sample-type of the FIR filter must match the sample-type supplied to this process callback");
|
||||
check();
|
||||
|
||||
auto&& inputBlock = context.getInputBlock();
|
||||
auto&& outputBlock = context.getOutputBlock();
|
||||
|
||||
// This class can only process mono signals. Use the ProcessorDuplicator class
|
||||
// to apply this filter on a multi-channel audio stream.
|
||||
jassert (inputBlock.getNumChannels() == 1);
|
||||
jassert (outputBlock.getNumChannels() == 1);
|
||||
|
||||
auto numSamples = inputBlock.getNumSamples();
|
||||
auto* src = inputBlock .getChannelPointer (0);
|
||||
auto* dst = outputBlock.getChannelPointer (0);
|
||||
|
||||
auto* fir = coefficients->getRawCoefficients();
|
||||
size_t p = pos;
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
fifo[p] = dst[i] = src[i];
|
||||
p = (p == 0 ? size - 1 : p - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
dst[i] = processSingleSample (src[i], fifo, fir, size, p);
|
||||
}
|
||||
|
||||
pos = p;
|
||||
}
|
||||
|
||||
|
||||
/** Processes a single sample, without any locking.
|
||||
Use this if you need processing of a single value.
|
||||
*/
|
||||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType sample) noexcept
|
||||
{
|
||||
check();
|
||||
return processSingleSample (sample, fifo, coefficients->getRawCoefficients(), size, pos);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
HeapBlock<SampleType> memory;
|
||||
SampleType* fifo = nullptr;
|
||||
size_t pos = 0, size = 0;
|
||||
|
||||
//==============================================================================
|
||||
void check()
|
||||
{
|
||||
jassert (coefficients != nullptr);
|
||||
|
||||
if (size != (coefficients->getFilterOrder() + 1))
|
||||
reset();
|
||||
}
|
||||
|
||||
static SampleType JUCE_VECTOR_CALLTYPE processSingleSample (SampleType sample, SampleType* buf,
|
||||
const NumericType* fir, size_t m, size_t& p) noexcept
|
||||
{
|
||||
SampleType out (0);
|
||||
|
||||
buf[p] = sample;
|
||||
|
||||
size_t k;
|
||||
for (k = 0; k < m - p; ++k)
|
||||
out += buf[(p + k)] * fir[k];
|
||||
|
||||
for (size_t j = 0; j < p; ++j)
|
||||
out += buf[j] * fir[j + k];
|
||||
|
||||
p = (p == 0 ? m - 1 : p - 1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
JUCE_LEAK_DETECTOR (Filter)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of coefficients for use in an FIRFilter object.
|
||||
|
||||
@see FIRFilter
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename NumericType>
|
||||
struct Coefficients : public ProcessorState
|
||||
{
|
||||
//==============================================================================
|
||||
/** Creates a null set of coefficients (which will produce silence). */
|
||||
Coefficients() : coefficients ({ NumericType() }) {}
|
||||
|
||||
/** Creates a null set of coefficients of a given size. */
|
||||
Coefficients (size_t size) { coefficients.resize ((int) size); }
|
||||
|
||||
/** Creates a set of coefficients from an array of samples. */
|
||||
Coefficients (const NumericType* samples, size_t numSamples) : coefficients (samples, (int) numSamples) {}
|
||||
|
||||
Coefficients (const Coefficients&) = default;
|
||||
Coefficients (Coefficients&&) = default;
|
||||
Coefficients& operator= (const Coefficients&) = default;
|
||||
Coefficients& operator= (Coefficients&&) = default;
|
||||
|
||||
/** The Coefficients structure is ref-counted, so this is a handy type that can be used
|
||||
as a pointer to one.
|
||||
*/
|
||||
using Ptr = ReferenceCountedObjectPtr<Coefficients>;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the filter order associated with the coefficients. */
|
||||
size_t getFilterOrder() const noexcept { return static_cast<size_t> (coefficients.size()) - 1; }
|
||||
|
||||
/** Returns the magnitude frequency response of the filter for a given frequency
|
||||
and sample rate.
|
||||
*/
|
||||
double getMagnitudeForFrequency (double frequency, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns the magnitude frequency response of the filter for a given frequency array
|
||||
and sample rate.
|
||||
*/
|
||||
void getMagnitudeForFrequencyArray (double* frequencies, double* magnitudes,
|
||||
size_t numSamples, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns the phase frequency response of the filter for a given frequency and
|
||||
sample rate.
|
||||
*/
|
||||
double getPhaseForFrequency (double frequency, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns the phase frequency response of the filter for a given frequency array
|
||||
and sample rate.
|
||||
*/
|
||||
void getPhaseForFrequencyArray (double* frequencies, double* phases,
|
||||
size_t numSamples, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns a raw data pointer to the coefficients. */
|
||||
NumericType* getRawCoefficients() noexcept { return coefficients.getRawDataPointer(); }
|
||||
|
||||
/** Returns a raw data pointer to the coefficients. */
|
||||
const NumericType* getRawCoefficients() const noexcept { return coefficients.begin(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Scales the values of the FIR filter with the sum of the squared coefficients. */
|
||||
void normalise() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The raw coefficients.
|
||||
You should leave these numbers alone unless you really know what you're doing.
|
||||
*/
|
||||
Array<NumericType> coefficients;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
222
deps/juce/modules/juce_dsp/processors/juce_FIRFilter_test.cpp
vendored
Normal file
222
deps/juce/modules/juce_dsp/processors/juce_FIRFilter_test.cpp
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class FIRFilterTest : public UnitTest
|
||||
{
|
||||
template <typename Type>
|
||||
struct Helpers
|
||||
{
|
||||
static void fillRandom (Random& random, Type* buffer, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
buffer[i] = (2.0f * random.nextFloat()) - 1.0f;
|
||||
}
|
||||
|
||||
static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
if (std::abs (a[i] - b[i]) > 1e-6f)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
template <typename Type>
|
||||
struct Helpers<SIMDRegister<Type>>
|
||||
{
|
||||
static void fillRandom (Random& random, SIMDRegister<Type>* buffer, size_t n)
|
||||
{
|
||||
Helpers<Type>::fillRandom (random, reinterpret_cast<Type*> (buffer), n * SIMDRegister<Type>::size());
|
||||
}
|
||||
|
||||
static bool checkArrayIsSimilar (SIMDRegister<Type>* a, SIMDRegister<Type>* b, size_t n) noexcept
|
||||
{
|
||||
return Helpers<Type>::checkArrayIsSimilar (reinterpret_cast<Type*> (a),
|
||||
reinterpret_cast<Type*> (b),
|
||||
n * SIMDRegister<Type>::size());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename Type>
|
||||
static void fillRandom (Random& random, Type* buffer, size_t n) { Helpers<Type>::fillRandom (random, buffer, n); }
|
||||
|
||||
template <typename Type>
|
||||
static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept { return Helpers<Type>::checkArrayIsSimilar (a, b, n); }
|
||||
|
||||
//==============================================================================
|
||||
// reference implementation of an FIR
|
||||
template <typename SampleType, typename NumericType>
|
||||
static void reference (const NumericType* firCoefficients, size_t numCoefficients,
|
||||
const SampleType* input, SampleType* output, size_t n) noexcept
|
||||
{
|
||||
if (numCoefficients == 0)
|
||||
{
|
||||
zeromem (output, sizeof (SampleType) * n);
|
||||
return;
|
||||
}
|
||||
|
||||
HeapBlock<SampleType> scratchBuffer (numCoefficients
|
||||
#if JUCE_USE_SIMD
|
||||
+ (SIMDRegister<NumericType>::SIMDRegisterSize / sizeof (SampleType))
|
||||
#endif
|
||||
);
|
||||
#if JUCE_USE_SIMD
|
||||
SampleType* buffer = reinterpret_cast<SampleType*> (SIMDRegister<NumericType>::getNextSIMDAlignedPtr (reinterpret_cast<NumericType*> (scratchBuffer.getData())));
|
||||
#else
|
||||
SampleType* buffer = scratchBuffer.getData();
|
||||
#endif
|
||||
|
||||
zeromem (buffer, sizeof (SampleType) * numCoefficients);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
for (size_t j = (numCoefficients - 1); j >= 1; --j)
|
||||
buffer[j] = buffer[j-1];
|
||||
|
||||
buffer[0] = input[i];
|
||||
|
||||
SampleType sum (0);
|
||||
|
||||
for (size_t j = 0; j < numCoefficients; ++j)
|
||||
sum += buffer[j] * firCoefficients[j];
|
||||
|
||||
output[i] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct LargeBlockTest
|
||||
{
|
||||
template <typename FloatType>
|
||||
static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
|
||||
{
|
||||
AudioBlock<const FloatType> input (&src, 1, n);
|
||||
AudioBlock<FloatType> output (&dst, 1, n);
|
||||
ProcessContextNonReplacing<FloatType> context (input, output);
|
||||
|
||||
filter.process (context);
|
||||
}
|
||||
};
|
||||
|
||||
struct SampleBySampleTest
|
||||
{
|
||||
template <typename FloatType>
|
||||
static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
dst[i] = filter.processSample (src[i]);
|
||||
}
|
||||
};
|
||||
|
||||
struct SplitBlockTest
|
||||
{
|
||||
template <typename FloatType>
|
||||
static void run (FIR::Filter<FloatType>& filter, FloatType* input, FloatType* output, size_t n)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < n; i += len)
|
||||
{
|
||||
len = jmin (n - i, n / 3);
|
||||
auto* src = input + i;
|
||||
auto* dst = output + i;
|
||||
|
||||
AudioBlock<const FloatType> inBlock (&src, 1, len);
|
||||
AudioBlock<FloatType> outBlock (&dst, 1, len);
|
||||
ProcessContextNonReplacing<FloatType> context (inBlock, outBlock);
|
||||
|
||||
filter.process (context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <typename TheTest, typename SampleType, typename NumericType>
|
||||
void runTestForType()
|
||||
{
|
||||
Random random (8392829);
|
||||
|
||||
for (auto size : {1, 2, 4, 8, 12, 13, 25})
|
||||
{
|
||||
constexpr size_t n = 813;
|
||||
|
||||
HeapBlock<char> inputBuffer, outputBuffer, refBuffer;
|
||||
AudioBlock<SampleType> input (inputBuffer, 1, n), output (outputBuffer, 1, n), ref (refBuffer, 1, n);
|
||||
fillRandom (random, input.getChannelPointer (0), n);
|
||||
|
||||
HeapBlock<char> firBlock;
|
||||
AudioBlock<NumericType> fir (firBlock, 1, static_cast<size_t> (size));
|
||||
fillRandom (random, fir.getChannelPointer (0), static_cast<size_t> (size));
|
||||
|
||||
FIR::Filter<SampleType> filter (*new FIR::Coefficients<NumericType> (fir.getChannelPointer (0), static_cast<size_t> (size)));
|
||||
ProcessSpec spec {0.0, n, 1};
|
||||
filter.prepare (spec);
|
||||
|
||||
reference<SampleType, NumericType> (fir.getChannelPointer (0), static_cast<size_t> (size),
|
||||
input.getChannelPointer (0), ref.getChannelPointer (0), n);
|
||||
|
||||
TheTest::template run<SampleType> (filter, input.getChannelPointer (0), output.getChannelPointer (0), n);
|
||||
expect (checkArrayIsSimilar (output.getChannelPointer (0), ref.getChannelPointer (0), n));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TheTest>
|
||||
void runTestForAllTypes (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
runTestForType<TheTest, float, float>();
|
||||
runTestForType<TheTest, double, double>();
|
||||
#if JUCE_USE_SIMD
|
||||
runTestForType<TheTest, SIMDRegister<float>, float>();
|
||||
runTestForType<TheTest, SIMDRegister<double>, double>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
FIRFilterTest()
|
||||
: UnitTest ("FIR Filter", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
runTestForAllTypes<LargeBlockTest> ("Large Blocks");
|
||||
runTestForAllTypes<SampleBySampleTest> ("Sample by Sample");
|
||||
runTestForAllTypes<SplitBlockTest> ("Split Block");
|
||||
}
|
||||
};
|
||||
|
||||
static FIRFilterTest firFilterUnitTest;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
122
deps/juce/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.cpp
vendored
Normal file
122
deps/juce/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.cpp
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
FirstOrderTPTFilter<SampleType>::FirstOrderTPTFilter()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::setType (Type newValue)
|
||||
{
|
||||
filterType = newValue;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::setCutoffFrequency (SampleType newValue)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newValue, static_cast<SampleType> (sampleRate * 0.5)));
|
||||
|
||||
cutoffFrequency = newValue;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
s1.resize (spec.numChannels);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::reset()
|
||||
{
|
||||
reset (static_cast<SampleType> (0));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::reset (SampleType newValue)
|
||||
{
|
||||
std::fill (s1.begin(), s1.end(), newValue);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
SampleType FirstOrderTPTFilter<SampleType>::processSample (int channel, SampleType inputValue)
|
||||
{
|
||||
auto& s = s1[(size_t) channel];
|
||||
|
||||
auto v = G * (inputValue - s);
|
||||
auto y = v + s;
|
||||
s = y + v;
|
||||
|
||||
switch (filterType)
|
||||
{
|
||||
case Type::lowpass: return y;
|
||||
case Type::highpass: return inputValue - y;
|
||||
case Type::allpass: return 2 * y - inputValue;
|
||||
default: break;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return y;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::snapToZero() noexcept
|
||||
{
|
||||
for (auto& s : s1)
|
||||
util::snapToZero (s);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void FirstOrderTPTFilter<SampleType>::update()
|
||||
{
|
||||
auto g = SampleType (std::tan (juce::MathConstants<double>::pi * cutoffFrequency / sampleRate));
|
||||
G = g / (1 + g);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class FirstOrderTPTFilter<float>;
|
||||
template class FirstOrderTPTFilter<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
151
deps/juce/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.h
vendored
Normal file
151
deps/juce/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.h
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class FirstOrderTPTFilterType
|
||||
{
|
||||
lowpass,
|
||||
highpass,
|
||||
allpass
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A first order filter class using the TPT (Topology-Preserving Transform) structure.
|
||||
|
||||
This filter can be modulated at high rates without producing audio artefacts. See
|
||||
Vadim Zavalishin's documentation about TPT structures for more information.
|
||||
|
||||
Note: Using this class prevents some loud audio artefacts commonly encountered when
|
||||
changing the cutoff frequency using of other filter simulation structures and IIR
|
||||
filter classes. However, this class may still require additional smoothing for
|
||||
cutoff frequency changes.
|
||||
|
||||
see StateVariableFilter, IIRFilter, SmoothedValue
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class FirstOrderTPTFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using Type = FirstOrderTPTFilterType;
|
||||
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
FirstOrderTPTFilter();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the filter type. */
|
||||
void setType (Type newType);
|
||||
|
||||
/** Sets the cutoff frequency of the filter.
|
||||
|
||||
@param newFrequencyHz cutoff frequency in Hz.
|
||||
*/
|
||||
void setCutoffFrequency (SampleType newFrequencyHz);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the type of the filter. */
|
||||
Type getType() const noexcept { return filterType; }
|
||||
|
||||
/** Returns the cutoff frequency of the filter. */
|
||||
SampleType getCutoffFrequency() const noexcept { return cutoffFrequency; }
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the filter. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the filter. */
|
||||
void reset();
|
||||
|
||||
/** Resets the internal state variables of the filter to a given value. */
|
||||
void reset (SampleType newValue);
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() <= s1.size());
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
|
||||
}
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Processes one sample at a time on a given channel. */
|
||||
SampleType processSample (int channel, SampleType inputValue);
|
||||
|
||||
/** Ensure that the state variables are rounded to zero if the state
|
||||
variables are denormals. This is only needed if you are doing
|
||||
sample by sample processing.
|
||||
*/
|
||||
void snapToZero() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
SampleType G = 0;
|
||||
std::vector<SampleType> s1 { 2 };
|
||||
double sampleRate = 44100.0;
|
||||
|
||||
//==============================================================================
|
||||
Type filterType = Type::lowpass;
|
||||
SampleType cutoffFrequency = 1000.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
577
deps/juce/modules/juce_dsp/processors/juce_IIRFilter.cpp
vendored
Normal file
577
deps/juce/modules/juce_dsp/processors/juce_IIRFilter.cpp
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 IIR
|
||||
{
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderLowPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
|
||||
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
|
||||
return { { n, n, n + 1, n - 1 } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderHighPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
|
||||
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
|
||||
return { { 1, -1, n + 1, n - 1 } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderAllPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
|
||||
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
|
||||
return { { n - 1, n + 1, n + 1, n - 1 } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeLowPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return makeLowPass (sampleRate, frequency, inverseRootTwo);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeLowPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
jassert (Q > 0.0);
|
||||
|
||||
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
auto nSquared = n * n;
|
||||
auto invQ = 1 / Q;
|
||||
auto c1 = 1 / (1 + invQ * n + nSquared);
|
||||
|
||||
return { { c1, c1 * 2, c1,
|
||||
1, c1 * 2 * (1 - nSquared),
|
||||
c1 * (1 - invQ * n + nSquared) } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeHighPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return makeHighPass (sampleRate, frequency, inverseRootTwo);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeHighPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
jassert (Q > 0.0);
|
||||
|
||||
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
auto nSquared = n * n;
|
||||
auto invQ = 1 / Q;
|
||||
auto c1 = 1 / (1 + invQ * n + nSquared);
|
||||
|
||||
return { { c1, c1 * -2, c1,
|
||||
1, c1 * 2 * (nSquared - 1),
|
||||
c1 * (1 - invQ * n + nSquared) } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeBandPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return makeBandPass (sampleRate, frequency, inverseRootTwo);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeBandPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
jassert (Q > 0.0);
|
||||
|
||||
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
auto nSquared = n * n;
|
||||
auto invQ = 1 / Q;
|
||||
auto c1 = 1 / (1 + invQ * n + nSquared);
|
||||
|
||||
return { { c1 * n * invQ, 0,
|
||||
-c1 * n * invQ, 1,
|
||||
c1 * 2 * (1 - nSquared),
|
||||
c1 * (1 - invQ * n + nSquared) } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeNotch (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return makeNotch (sampleRate, frequency, inverseRootTwo);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeNotch (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
|
||||
jassert (Q > 0.0);
|
||||
|
||||
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
auto nSquared = n * n;
|
||||
auto invQ = 1 / Q;
|
||||
auto c1 = 1 / (1 + n * invQ + nSquared);
|
||||
auto b0 = c1 * (1 + nSquared);
|
||||
auto b1 = 2 * c1 * (1 - nSquared);
|
||||
|
||||
return { { b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared) } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeAllPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return makeAllPass (sampleRate, frequency, inverseRootTwo);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeAllPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= sampleRate * 0.5);
|
||||
jassert (Q > 0);
|
||||
|
||||
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
|
||||
auto nSquared = n * n;
|
||||
auto invQ = 1 / Q;
|
||||
auto c1 = 1 / (1 + invQ * n + nSquared);
|
||||
auto b0 = c1 * (1 - n * invQ + nSquared);
|
||||
auto b1 = c1 * 2 * (1 - nSquared);
|
||||
|
||||
return { { b0, b1, 1, 1, b1, b0 } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeLowShelf (double sampleRate,
|
||||
NumericType cutOffFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
|
||||
jassert (Q > 0.0);
|
||||
|
||||
auto A = jmax (static_cast<NumericType> (0.0), std::sqrt (gainFactor));
|
||||
auto aminus1 = A - 1;
|
||||
auto aplus1 = A + 1;
|
||||
auto omega = (2 * MathConstants<NumericType>::pi * jmax (cutOffFrequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate);
|
||||
auto coso = std::cos (omega);
|
||||
auto beta = std::sin (omega) * std::sqrt (A) / Q;
|
||||
auto aminus1TimesCoso = aminus1 * coso;
|
||||
|
||||
return { { A * (aplus1 - aminus1TimesCoso + beta),
|
||||
A * 2 * (aminus1 - aplus1 * coso),
|
||||
A * (aplus1 - aminus1TimesCoso - beta),
|
||||
aplus1 + aminus1TimesCoso + beta,
|
||||
-2 * (aminus1 + aplus1 * coso),
|
||||
aplus1 + aminus1TimesCoso - beta } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeHighShelf (double sampleRate,
|
||||
NumericType cutOffFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (cutOffFrequency > 0 && cutOffFrequency <= static_cast<NumericType> (sampleRate * 0.5));
|
||||
jassert (Q > 0);
|
||||
|
||||
auto A = jmax (static_cast<NumericType> (0.0), std::sqrt (gainFactor));
|
||||
auto aminus1 = A - 1;
|
||||
auto aplus1 = A + 1;
|
||||
auto omega = (2 * MathConstants<NumericType>::pi * jmax (cutOffFrequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate);
|
||||
auto coso = std::cos (omega);
|
||||
auto beta = std::sin (omega) * std::sqrt (A) / Q;
|
||||
auto aminus1TimesCoso = aminus1 * coso;
|
||||
|
||||
return { { A * (aplus1 + aminus1TimesCoso + beta),
|
||||
A * -2 * (aminus1 + aplus1 * coso),
|
||||
A * (aplus1 + aminus1TimesCoso - beta),
|
||||
aplus1 - aminus1TimesCoso + beta,
|
||||
2 * (aminus1 - aplus1 * coso),
|
||||
aplus1 - aminus1TimesCoso - beta } };
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
std::array<NumericType, 6> ArrayCoefficients<NumericType>::makePeakFilter (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor)
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (frequency > 0 && frequency <= static_cast<NumericType> (sampleRate * 0.5));
|
||||
jassert (Q > 0);
|
||||
jassert (gainFactor > 0);
|
||||
|
||||
auto A = jmax (static_cast<NumericType> (0.0), std::sqrt (gainFactor));
|
||||
auto omega = (2 * MathConstants<NumericType>::pi * jmax (frequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate);
|
||||
auto alpha = std::sin (omega) / (Q * 2);
|
||||
auto c2 = -2 * std::cos (omega);
|
||||
auto alphaTimesA = alpha * A;
|
||||
auto alphaOverA = alpha / A;
|
||||
|
||||
return { { 1 + alphaTimesA, c2, 1 - alphaTimesA, 1 + alphaOverA, c2, 1 - alphaOverA } };
|
||||
}
|
||||
|
||||
template struct ArrayCoefficients<float>;
|
||||
template struct ArrayCoefficients<double>;
|
||||
|
||||
//==============================================================================
|
||||
template <typename NumericType>
|
||||
Coefficients<NumericType>::Coefficients()
|
||||
{
|
||||
assign ({ NumericType(), NumericType(), NumericType(),
|
||||
NumericType(), NumericType(), NumericType() });
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
Coefficients<NumericType>::Coefficients (NumericType b0, NumericType b1,
|
||||
NumericType a0, NumericType a1)
|
||||
{
|
||||
assign ({ b0, b1,
|
||||
a0, a1 });
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
Coefficients<NumericType>::Coefficients (NumericType b0, NumericType b1, NumericType b2,
|
||||
NumericType a0, NumericType a1, NumericType a2)
|
||||
{
|
||||
assign ({ b0, b1, b2,
|
||||
a0, a1, a2 });
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
Coefficients<NumericType>::Coefficients (NumericType b0, NumericType b1, NumericType b2, NumericType b3,
|
||||
NumericType a0, NumericType a1, NumericType a2, NumericType a3)
|
||||
{
|
||||
assign ({ b0, b1, b2, b3,
|
||||
a0, a1, a2, a3 });
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeFirstOrderLowPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeFirstOrderLowPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeFirstOrderHighPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeFirstOrderHighPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeFirstOrderAllPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeFirstOrderAllPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeLowPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeLowPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeLowPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeLowPass (sampleRate, frequency, Q));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeHighPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeHighPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeHighPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeHighPass (sampleRate, frequency, Q));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeBandPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeBandPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeBandPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeBandPass (sampleRate, frequency, Q));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeNotch (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeNotch (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeNotch (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeNotch (sampleRate, frequency, Q));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeAllPass (double sampleRate,
|
||||
NumericType frequency)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeAllPass (sampleRate, frequency));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeAllPass (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeAllPass (sampleRate, frequency, Q));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeLowShelf (double sampleRate,
|
||||
NumericType cutOffFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeLowShelf (sampleRate, cutOffFrequency, Q, gainFactor));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makeHighShelf (double sampleRate,
|
||||
NumericType cutOffFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makeHighShelf (sampleRate, cutOffFrequency, Q, gainFactor));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
typename Coefficients<NumericType>::Ptr Coefficients<NumericType>::makePeakFilter (double sampleRate,
|
||||
NumericType frequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor)
|
||||
{
|
||||
return *new Coefficients (ArrayCoeffs::makePeakFilter (sampleRate, frequency, Q, gainFactor));
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
size_t Coefficients<NumericType>::getFilterOrder() const noexcept
|
||||
{
|
||||
return (static_cast<size_t> (coefficients.size()) - 1) / 2;
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
double Coefficients<NumericType>::getMagnitudeForFrequency (double frequency, double sampleRate) const noexcept
|
||||
{
|
||||
constexpr Complex<double> j (0, 1);
|
||||
const auto order = getFilterOrder();
|
||||
const auto* coefs = coefficients.begin();
|
||||
|
||||
jassert (frequency >= 0 && frequency <= sampleRate * 0.5);
|
||||
|
||||
Complex<double> numerator = 0.0, denominator = 0.0, factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequency * j / sampleRate);
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
denominator = 1.0;
|
||||
factor = jw;
|
||||
|
||||
for (size_t n = order + 1; n <= 2 * order; ++n)
|
||||
{
|
||||
denominator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
return std::abs (numerator / denominator);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
void Coefficients<NumericType>::getMagnitudeForFrequencyArray (const double* frequencies, double* magnitudes,
|
||||
size_t numSamples, double sampleRate) const noexcept
|
||||
{
|
||||
constexpr Complex<double> j (0, 1);
|
||||
const auto order = getFilterOrder();
|
||||
const auto* coefs = coefficients.begin();
|
||||
|
||||
jassert (order >= 0);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
jassert (frequencies[i] >= 0 && frequencies[i] <= sampleRate * 0.5);
|
||||
|
||||
Complex<double> numerator = 0.0, denominator = 0.0, factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequencies[i] * j / sampleRate);
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
denominator = 1.0;
|
||||
factor = jw;
|
||||
|
||||
for (size_t n = order + 1; n <= 2 * order; ++n)
|
||||
{
|
||||
denominator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
magnitudes[i] = std::abs(numerator / denominator);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
double Coefficients<NumericType>::getPhaseForFrequency (double frequency, double sampleRate) const noexcept
|
||||
{
|
||||
constexpr Complex<double> j (0, 1);
|
||||
const auto order = getFilterOrder();
|
||||
const auto* coefs = coefficients.begin();
|
||||
|
||||
jassert (frequency >= 0 && frequency <= sampleRate * 0.5);
|
||||
|
||||
Complex<double> numerator = 0.0, denominator = 0.0, factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequency * j / sampleRate);
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
denominator = 1.0;
|
||||
factor = jw;
|
||||
|
||||
for (size_t n = order + 1; n <= 2 * order; ++n)
|
||||
{
|
||||
denominator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
return std::arg (numerator / denominator);
|
||||
}
|
||||
|
||||
template <typename NumericType>
|
||||
void Coefficients<NumericType>::getPhaseForFrequencyArray (double* frequencies, double* phases,
|
||||
size_t numSamples, double sampleRate) const noexcept
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
|
||||
constexpr Complex<double> j (0, 1);
|
||||
const auto order = getFilterOrder();
|
||||
const auto* coefs = coefficients.begin();
|
||||
auto invSampleRate = 1 / sampleRate;
|
||||
|
||||
jassert (order >= 0);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
jassert (frequencies[i] >= 0 && frequencies[i] <= sampleRate * 0.5);
|
||||
|
||||
Complex<double> numerator = 0.0, denominator = 0.0, factor = 1.0;
|
||||
Complex<double> jw = std::exp (-MathConstants<double>::twoPi * frequencies[i] * j * invSampleRate);
|
||||
|
||||
for (size_t n = 0; n <= order; ++n)
|
||||
{
|
||||
numerator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
denominator = 1.0;
|
||||
factor = jw;
|
||||
|
||||
for (size_t n = order + 1; n <= 2 * order; ++n)
|
||||
{
|
||||
denominator += static_cast<double> (coefs[n]) * factor;
|
||||
factor *= jw;
|
||||
}
|
||||
|
||||
phases[i] = std::arg (numerator / denominator);
|
||||
}
|
||||
}
|
||||
|
||||
template struct Coefficients<float>;
|
||||
template struct Coefficients<double>;
|
||||
|
||||
} // namespace IIR
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
403
deps/juce/modules/juce_dsp/processors/juce_IIRFilter.h
vendored
Normal file
403
deps/juce/modules/juce_dsp/processors/juce_IIRFilter.h
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
/**
|
||||
Classes for IIR filter processing.
|
||||
*/
|
||||
namespace IIR
|
||||
{
|
||||
/** A set of coefficients for use in an Filter object.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename NumericType>
|
||||
struct ArrayCoefficients
|
||||
{
|
||||
/** Returns the coefficients for a first order low-pass filter. */
|
||||
static std::array<NumericType, 4> makeFirstOrderLowPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a first order high-pass filter. */
|
||||
static std::array<NumericType, 4> makeFirstOrderHighPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a first order all-pass filter. */
|
||||
static std::array<NumericType, 4> makeFirstOrderAllPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a low-pass filter. */
|
||||
static std::array<NumericType, 6> makeLowPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a low-pass filter with variable Q. */
|
||||
static std::array<NumericType, 6> makeLowPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
/** Returns the coefficients for a high-pass filter. */
|
||||
static std::array<NumericType, 6> makeHighPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a high-pass filter with variable Q. */
|
||||
static std::array<NumericType, 6> makeHighPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
/** Returns the coefficients for a band-pass filter. */
|
||||
static std::array<NumericType, 6> makeBandPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a band-pass filter with variable Q. */
|
||||
static std::array<NumericType, 6> makeBandPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
/** Returns the coefficients for a notch filter. */
|
||||
static std::array<NumericType, 6> makeNotch (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a notch filter with variable Q. */
|
||||
static std::array<NumericType, 6> makeNotch (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
/** Returns the coefficients for an all-pass filter. */
|
||||
static std::array<NumericType, 6> makeAllPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for an all-pass filter with variable Q. */
|
||||
static std::array<NumericType, 6> makeAllPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
/** Returns the coefficients for a low-pass shelf filter with variable Q and gain.
|
||||
|
||||
The gain is a scale factor that the low frequencies are multiplied by, so values
|
||||
greater than 1.0 will boost the low frequencies, values less than 1.0 will
|
||||
attenuate them.
|
||||
*/
|
||||
static std::array<NumericType, 6> makeLowShelf (double sampleRate,
|
||||
NumericType cutOffFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor);
|
||||
|
||||
/** Returns the coefficients for a high-pass shelf filter with variable Q and gain.
|
||||
|
||||
The gain is a scale factor that the high frequencies are multiplied by, so values
|
||||
greater than 1.0 will boost the high frequencies, values less than 1.0 will
|
||||
attenuate them.
|
||||
*/
|
||||
static std::array<NumericType, 6> makeHighShelf (double sampleRate,
|
||||
NumericType cutOffFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor);
|
||||
|
||||
/** Returns the coefficients for a peak filter centred around a
|
||||
given frequency, with a variable Q and gain.
|
||||
|
||||
The gain is a scale factor that the centre frequencies are multiplied by, so
|
||||
values greater than 1.0 will boost the centre frequencies, values less than
|
||||
1.0 will attenuate them.
|
||||
*/
|
||||
static std::array<NumericType, 6> makePeakFilter (double sampleRate,
|
||||
NumericType centreFrequency,
|
||||
NumericType Q,
|
||||
NumericType gainFactor);
|
||||
|
||||
private:
|
||||
// Unfortunately, std::sqrt is not marked as constexpr just yet in all compilers
|
||||
static constexpr NumericType inverseRootTwo = static_cast<NumericType> (0.70710678118654752440L);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A set of coefficients for use in an Filter object.
|
||||
@see IIR::Filter
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename NumericType>
|
||||
struct Coefficients : public ProcessorState
|
||||
{
|
||||
/** Creates a null set of coefficients (which will produce silence). */
|
||||
Coefficients();
|
||||
|
||||
/** Directly constructs an object from the raw coefficients.
|
||||
Most people will want to use the static methods instead of this, but the
|
||||
constructor is public to allow tinkerers to create their own custom filters!
|
||||
*/
|
||||
Coefficients (NumericType b0, NumericType b1,
|
||||
NumericType a0, NumericType a1);
|
||||
|
||||
Coefficients (NumericType b0, NumericType b1, NumericType b2,
|
||||
NumericType a0, NumericType a1, NumericType a2);
|
||||
|
||||
Coefficients (NumericType b0, NumericType b1, NumericType b2, NumericType b3,
|
||||
NumericType a0, NumericType a1, NumericType a2, NumericType a3);
|
||||
|
||||
Coefficients (const Coefficients&) = default;
|
||||
Coefficients (Coefficients&&) = default;
|
||||
Coefficients& operator= (const Coefficients&) = default;
|
||||
Coefficients& operator= (Coefficients&&) = default;
|
||||
|
||||
/** Constructs from an array. */
|
||||
template <size_t Num>
|
||||
explicit Coefficients (const std::array<NumericType, Num>& values) { assignImpl<Num> (values.data()); }
|
||||
|
||||
/** Assigns contents from an array. */
|
||||
template <size_t Num>
|
||||
Coefficients& operator= (const std::array<NumericType, Num>& values) { return assignImpl<Num> (values.data()); }
|
||||
|
||||
/** The Coefficients structure is ref-counted, so this is a handy type that can be used
|
||||
as a pointer to one.
|
||||
*/
|
||||
using Ptr = ReferenceCountedObjectPtr<Coefficients>;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for a first order low-pass filter. */
|
||||
static Ptr makeFirstOrderLowPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a first order high-pass filter. */
|
||||
static Ptr makeFirstOrderHighPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a first order all-pass filter. */
|
||||
static Ptr makeFirstOrderAllPass (double sampleRate, NumericType frequency);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for a low-pass filter. */
|
||||
static Ptr makeLowPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a low-pass filter with variable Q. */
|
||||
static Ptr makeLowPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for a high-pass filter. */
|
||||
static Ptr makeHighPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a high-pass filter with variable Q. */
|
||||
static Ptr makeHighPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for a band-pass filter. */
|
||||
static Ptr makeBandPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a band-pass filter with variable Q. */
|
||||
static Ptr makeBandPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for a notch filter. */
|
||||
static Ptr makeNotch (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for a notch filter with variable Q. */
|
||||
static Ptr makeNotch (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for an all-pass filter. */
|
||||
static Ptr makeAllPass (double sampleRate, NumericType frequency);
|
||||
|
||||
/** Returns the coefficients for an all-pass filter with variable Q. */
|
||||
static Ptr makeAllPass (double sampleRate, NumericType frequency, NumericType Q);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the coefficients for a low-pass shelf filter with variable Q and gain.
|
||||
|
||||
The gain is a scale factor that the low frequencies are multiplied by, so values
|
||||
greater than 1.0 will boost the low frequencies, values less than 1.0 will
|
||||
attenuate them.
|
||||
*/
|
||||
static Ptr makeLowShelf (double sampleRate, NumericType cutOffFrequency,
|
||||
NumericType Q, NumericType gainFactor);
|
||||
|
||||
/** Returns the coefficients for a high-pass shelf filter with variable Q and gain.
|
||||
|
||||
The gain is a scale factor that the high frequencies are multiplied by, so values
|
||||
greater than 1.0 will boost the high frequencies, values less than 1.0 will
|
||||
attenuate them.
|
||||
*/
|
||||
static Ptr makeHighShelf (double sampleRate, NumericType cutOffFrequency,
|
||||
NumericType Q, NumericType gainFactor);
|
||||
|
||||
/** Returns the coefficients for a peak filter centred around a
|
||||
given frequency, with a variable Q and gain.
|
||||
|
||||
The gain is a scale factor that the centre frequencies are multiplied by, so
|
||||
values greater than 1.0 will boost the centre frequencies, values less than
|
||||
1.0 will attenuate them.
|
||||
*/
|
||||
static Ptr makePeakFilter (double sampleRate, NumericType centreFrequency,
|
||||
NumericType Q, NumericType gainFactor);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the filter order associated with the coefficients */
|
||||
size_t getFilterOrder() const noexcept;
|
||||
|
||||
/** Returns the magnitude frequency response of the filter for a given frequency
|
||||
and sample rate
|
||||
*/
|
||||
double getMagnitudeForFrequency (double frequency, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns the magnitude frequency response of the filter for a given frequency array
|
||||
and sample rate.
|
||||
*/
|
||||
void getMagnitudeForFrequencyArray (const double* frequencies, double* magnitudes,
|
||||
size_t numSamples, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns the phase frequency response of the filter for a given frequency and
|
||||
sample rate
|
||||
*/
|
||||
double getPhaseForFrequency (double frequency, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns the phase frequency response of the filter for a given frequency array
|
||||
and sample rate.
|
||||
*/
|
||||
void getPhaseForFrequencyArray (double* frequencies, double* phases,
|
||||
size_t numSamples, double sampleRate) const noexcept;
|
||||
|
||||
/** Returns a raw data pointer to the coefficients. */
|
||||
NumericType* getRawCoefficients() noexcept { return coefficients.getRawDataPointer(); }
|
||||
|
||||
/** Returns a raw data pointer to the coefficients. */
|
||||
const NumericType* getRawCoefficients() const noexcept { return coefficients.begin(); }
|
||||
|
||||
//==============================================================================
|
||||
/** The raw coefficients.
|
||||
You should leave these numbers alone unless you really know what you're doing.
|
||||
*/
|
||||
Array<NumericType> coefficients;
|
||||
|
||||
private:
|
||||
using ArrayCoeffs = ArrayCoefficients<NumericType>;
|
||||
|
||||
template <size_t Num>
|
||||
Coefficients& assignImpl (const NumericType* values);
|
||||
|
||||
template <size_t Num>
|
||||
Coefficients& assign (const NumericType (& values)[Num]) { return assignImpl<Num> (values); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A processing class that can perform IIR filtering on an audio signal, using
|
||||
the Transposed Direct Form II digital structure.
|
||||
|
||||
If you need a lowpass, bandpass or highpass filter with fast modulation of
|
||||
its cutoff frequency, you might use the class StateVariableFilter instead,
|
||||
which is designed to prevent artefacts at parameter changes, instead of the
|
||||
class Filter.
|
||||
|
||||
@see Filter::Coefficients, FilterAudioSource, StateVariableFilter
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
/** The NumericType is the underlying primitive type used by the SampleType (which
|
||||
could be either a primitive or vector)
|
||||
*/
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
/** A typedef for a ref-counted pointer to the coefficients object */
|
||||
using CoefficientsPtr = typename Coefficients<NumericType>::Ptr;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a filter.
|
||||
|
||||
Initially the filter is inactive, so will have no effect on samples that
|
||||
you process with it. You can modify the coefficients member to turn it into
|
||||
the type of filter needed.
|
||||
*/
|
||||
Filter();
|
||||
|
||||
/** Creates a filter with a given set of coefficients. */
|
||||
Filter (CoefficientsPtr coefficientsToUse);
|
||||
|
||||
Filter (const Filter&) = default;
|
||||
Filter (Filter&&) = default;
|
||||
Filter& operator= (const Filter&) = default;
|
||||
Filter& operator= (Filter&&) = default;
|
||||
|
||||
//==============================================================================
|
||||
/** The coefficients of the IIR filter. It's up to the caller to ensure that
|
||||
these coefficients are modified in a thread-safe way.
|
||||
|
||||
If you change the order of the coefficients then you must call reset after
|
||||
modifying them.
|
||||
*/
|
||||
CoefficientsPtr coefficients;
|
||||
|
||||
//==============================================================================
|
||||
/** Resets the filter's processing pipeline, ready to start a new stream of data.
|
||||
|
||||
Note that this clears the processing state, but the type of filter and
|
||||
its coefficients aren't changed.
|
||||
*/
|
||||
void reset() { reset (SampleType {0}); }
|
||||
|
||||
/** Resets the filter's processing pipeline to a specific value.
|
||||
@see reset
|
||||
*/
|
||||
void reset (SampleType resetToValue);
|
||||
|
||||
//==============================================================================
|
||||
/** Called before processing starts. */
|
||||
void prepare (const ProcessSpec&) noexcept;
|
||||
|
||||
/** Processes a block of samples */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
if (context.isBypassed)
|
||||
processInternal<ProcessContext, true> (context);
|
||||
else
|
||||
processInternal<ProcessContext, false> (context);
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Processes a single sample, without any locking.
|
||||
|
||||
Use this if you need processing of a single value.
|
||||
|
||||
Moreover, you might need the function snapToZero after a few calls to avoid
|
||||
potential denormalisation issues.
|
||||
*/
|
||||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType sample) noexcept;
|
||||
|
||||
/** Ensure that the state variables are rounded to zero if the state
|
||||
variables are denormals. This is only needed if you are doing
|
||||
sample by sample processing.
|
||||
*/
|
||||
void snapToZero() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void check();
|
||||
|
||||
/** Processes a block of samples */
|
||||
template <typename ProcessContext, bool isBypassed>
|
||||
void processInternal (const ProcessContext& context) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
HeapBlock<SampleType> memory;
|
||||
SampleType* state = nullptr;
|
||||
size_t order = 0;
|
||||
|
||||
JUCE_LEAK_DETECTOR (Filter)
|
||||
};
|
||||
} // namespace IIR
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
||||
#include "juce_IIRFilter_Impl.h"
|
||||
245
deps/juce/modules/juce_dsp/processors/juce_IIRFilter_Impl.h
vendored
Normal file
245
deps/juce/modules/juce_dsp/processors/juce_IIRFilter_Impl.h
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 IIR
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
template <typename NumericType>
|
||||
template <size_t Num>
|
||||
Coefficients<NumericType>& Coefficients<NumericType>::assignImpl (const NumericType* values)
|
||||
{
|
||||
static_assert (Num % 2 == 0, "Must supply an even number of coefficients");
|
||||
const auto a0Index = Num / 2;
|
||||
const auto a0 = values[a0Index];
|
||||
const auto a0Inv = a0 != NumericType() ? static_cast<NumericType> (1) / values[a0Index]
|
||||
: NumericType();
|
||||
|
||||
coefficients.clearQuick();
|
||||
coefficients.ensureStorageAllocated ((int) jmax ((size_t) 8, Num));
|
||||
|
||||
for (size_t i = 0; i < Num; ++i)
|
||||
if (i != a0Index)
|
||||
coefficients.add (values[i] * a0Inv);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
Filter<SampleType>::Filter()
|
||||
: coefficients (new Coefficients<typename Filter<SampleType>::NumericType> (1, 0, 1, 0))
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
Filter<SampleType>::Filter (CoefficientsPtr c) : coefficients (std::move (c))
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Filter<SampleType>::reset (SampleType resetToValue)
|
||||
{
|
||||
auto newOrder = coefficients->getFilterOrder();
|
||||
|
||||
if (newOrder != order)
|
||||
{
|
||||
memory.malloc (jmax (order, newOrder, static_cast<size_t> (3)) + 1);
|
||||
state = snapPointerToAlignment (memory.getData(), sizeof (SampleType));
|
||||
order = newOrder;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < order; ++i)
|
||||
state[i] = resetToValue;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Filter<SampleType>::prepare (const ProcessSpec&) noexcept { reset(); }
|
||||
|
||||
template <typename SampleType>
|
||||
template <typename ProcessContext, bool bypassed>
|
||||
void Filter<SampleType>::processInternal (const ProcessContext& context) noexcept
|
||||
{
|
||||
static_assert (std::is_same<typename ProcessContext::SampleType, SampleType>::value,
|
||||
"The sample-type of the IIR filter must match the sample-type supplied to this process callback");
|
||||
check();
|
||||
|
||||
auto&& inputBlock = context.getInputBlock();
|
||||
auto&& outputBlock = context.getOutputBlock();
|
||||
|
||||
// This class can only process mono signals. Use the ProcessorDuplicator class
|
||||
// to apply this filter on a multi-channel audio stream.
|
||||
jassert (inputBlock.getNumChannels() == 1);
|
||||
jassert (outputBlock.getNumChannels() == 1);
|
||||
|
||||
auto numSamples = inputBlock.getNumSamples();
|
||||
auto* src = inputBlock .getChannelPointer (0);
|
||||
auto* dst = outputBlock.getChannelPointer (0);
|
||||
auto* coeffs = coefficients->getRawCoefficients();
|
||||
|
||||
switch (order)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
auto b0 = coeffs[0];
|
||||
auto b1 = coeffs[1];
|
||||
auto a1 = coeffs[2];
|
||||
|
||||
auto lv1 = state[0];
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto input = src[i];
|
||||
auto output = input * b0 + lv1;
|
||||
|
||||
dst[i] = bypassed ? input : output;
|
||||
|
||||
lv1 = (input * b1) - (output * a1);
|
||||
}
|
||||
|
||||
util::snapToZero (lv1); state[0] = lv1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
auto b0 = coeffs[0];
|
||||
auto b1 = coeffs[1];
|
||||
auto b2 = coeffs[2];
|
||||
auto a1 = coeffs[3];
|
||||
auto a2 = coeffs[4];
|
||||
|
||||
auto lv1 = state[0];
|
||||
auto lv2 = state[1];
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto input = src[i];
|
||||
auto output = (input * b0) + lv1;
|
||||
dst[i] = bypassed ? input : output;
|
||||
|
||||
lv1 = (input * b1) - (output* a1) + lv2;
|
||||
lv2 = (input * b2) - (output* a2);
|
||||
}
|
||||
|
||||
util::snapToZero (lv1); state[0] = lv1;
|
||||
util::snapToZero (lv2); state[1] = lv2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
auto b0 = coeffs[0];
|
||||
auto b1 = coeffs[1];
|
||||
auto b2 = coeffs[2];
|
||||
auto b3 = coeffs[3];
|
||||
auto a1 = coeffs[4];
|
||||
auto a2 = coeffs[5];
|
||||
auto a3 = coeffs[6];
|
||||
|
||||
auto lv1 = state[0];
|
||||
auto lv2 = state[1];
|
||||
auto lv3 = state[2];
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto input = src[i];
|
||||
auto output = (input * b0) + lv1;
|
||||
dst[i] = bypassed ? input : output;
|
||||
|
||||
lv1 = (input * b1) - (output* a1) + lv2;
|
||||
lv2 = (input * b2) - (output* a2) + lv3;
|
||||
lv3 = (input * b3) - (output* a3);
|
||||
}
|
||||
|
||||
util::snapToZero (lv1); state[0] = lv1;
|
||||
util::snapToZero (lv2); state[1] = lv2;
|
||||
util::snapToZero (lv3); state[2] = lv3;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto input = src[i];
|
||||
auto output= (input * coeffs[0]) + state[0];
|
||||
dst[i] = bypassed ? input : output;
|
||||
|
||||
for (size_t j = 0; j < order - 1; ++j)
|
||||
state[j] = (input * coeffs[j + 1]) - (output* coeffs[order + j + 1]) + state[j + 1];
|
||||
|
||||
state[order - 1] = (input * coeffs[order]) - (output* coeffs[order * 2]);
|
||||
}
|
||||
|
||||
snapToZero();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
SampleType JUCE_VECTOR_CALLTYPE Filter<SampleType>::processSample (SampleType sample) noexcept
|
||||
{
|
||||
check();
|
||||
auto* c = coefficients->getRawCoefficients();
|
||||
|
||||
auto output = (c[0] * sample) + state[0];
|
||||
|
||||
for (size_t j = 0; j < order - 1; ++j)
|
||||
state[j] = (c[j + 1] * sample) - (c[order + j + 1] * output) + state[j + 1];
|
||||
|
||||
state[order - 1] = (c[order] * sample) - (c[order * 2] * output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Filter<SampleType>::snapToZero() noexcept
|
||||
{
|
||||
for (size_t i = 0; i < order; ++i)
|
||||
util::snapToZero (state[i]);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Filter<SampleType>::check()
|
||||
{
|
||||
jassert (coefficients != nullptr);
|
||||
|
||||
if (order != coefficients->getFilterOrder())
|
||||
reset();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace IIR
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
149
deps/juce/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.cpp
vendored
Normal file
149
deps/juce/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.cpp
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
LinkwitzRileyFilter<SampleType>::LinkwitzRileyFilter()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::setType (Type newType)
|
||||
{
|
||||
filterType = newType;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::setCutoffFrequency (SampleType newCutoffFrequencyHz)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newCutoffFrequencyHz, static_cast<SampleType> (sampleRate * 0.5)));
|
||||
|
||||
cutoffFrequency = newCutoffFrequencyHz;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
update();
|
||||
|
||||
s1.resize (spec.numChannels);
|
||||
s2.resize (spec.numChannels);
|
||||
s3.resize (spec.numChannels);
|
||||
s4.resize (spec.numChannels);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::reset()
|
||||
{
|
||||
for (auto s : { &s1, &s2, &s3, &s4 })
|
||||
std::fill (s->begin(), s->end(), static_cast<SampleType> (0));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::snapToZero() noexcept
|
||||
{
|
||||
for (auto s : { &s1, &s2, &s3, &s4 })
|
||||
for (auto& element : *s)
|
||||
util::snapToZero (element);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
SampleType LinkwitzRileyFilter<SampleType>::processSample (int channel, SampleType inputValue)
|
||||
{
|
||||
auto yH = (inputValue - (R2 + g) * s1[(size_t) channel] - s2[(size_t) channel]) * h;
|
||||
|
||||
auto yB = g * yH + s1[(size_t) channel];
|
||||
s1[(size_t) channel] = g * yH + yB;
|
||||
|
||||
auto yL = g * yB + s2[(size_t) channel];
|
||||
s2[(size_t) channel] = g * yB + yL;
|
||||
|
||||
if (filterType == Type::allpass)
|
||||
return yL - R2 * yB + yH;
|
||||
|
||||
auto yH2 = ((filterType == Type::lowpass ? yL : yH) - (R2 + g) * s3[(size_t) channel] - s4[(size_t) channel]) * h;
|
||||
|
||||
auto yB2 = g * yH2 + s3[(size_t) channel];
|
||||
s3[(size_t) channel] = g * yH2 + yB2;
|
||||
|
||||
auto yL2 = g * yB2 + s4[(size_t) channel];
|
||||
s4[(size_t) channel] = g * yB2 + yL2;
|
||||
|
||||
return filterType == Type::lowpass ? yL2 : yH2;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::processSample (int channel, SampleType inputValue, SampleType &outputLow, SampleType &outputHigh)
|
||||
{
|
||||
auto yH = (inputValue - (R2 + g) * s1[(size_t) channel] - s2[(size_t) channel]) * h;
|
||||
|
||||
auto yB = g * yH + s1[(size_t) channel];
|
||||
s1[(size_t) channel] = g * yH + yB;
|
||||
|
||||
auto yL = g * yB + s2[(size_t) channel];
|
||||
s2[(size_t) channel] = g * yB + yL;
|
||||
|
||||
auto yH2 = (yL - (R2 + g) * s3[(size_t) channel] - s4[(size_t) channel]) * h;
|
||||
|
||||
auto yB2 = g * yH2 + s3[(size_t) channel];
|
||||
s3[(size_t) channel] = g * yH2 + yB2;
|
||||
|
||||
auto yL2 = g * yB2 + s4[(size_t) channel];
|
||||
s4[(size_t) channel] = g * yB2 + yL2;
|
||||
|
||||
outputLow = yL2;
|
||||
outputHigh = yL - R2 * yB + yH - yL2;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void LinkwitzRileyFilter<SampleType>::update()
|
||||
{
|
||||
g = (SampleType) std::tan (MathConstants<double>::pi * cutoffFrequency / sampleRate);
|
||||
R2 = (SampleType) std::sqrt (2.0);
|
||||
h = (SampleType) (1.0 / (1.0 + R2 * g + g * g));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class LinkwitzRileyFilter<float>;
|
||||
template class LinkwitzRileyFilter<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
143
deps/juce/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.h
vendored
Normal file
143
deps/juce/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.h
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class LinkwitzRileyFilterType
|
||||
{
|
||||
lowpass,
|
||||
highpass,
|
||||
allpass
|
||||
};
|
||||
|
||||
/**
|
||||
A filter class designed to perform multi-band separation using the TPT
|
||||
(Topology-Preserving Transform) structure.
|
||||
|
||||
Linkwitz-Riley filters are widely used in audio crossovers that have two outputs,
|
||||
a low-pass and a high-pass, such that their sum is equivalent to an all-pass filter
|
||||
with a flat magnitude frequency response. The Linkwitz-Riley filters available in
|
||||
this class are designed to have a -24 dB/octave slope (LR 4th order).
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class LinkwitzRileyFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using Type = LinkwitzRileyFilterType;
|
||||
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
LinkwitzRileyFilter();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the filter type. */
|
||||
void setType (Type newType);
|
||||
|
||||
/** Sets the cutoff frequency of the filter in Hz. */
|
||||
void setCutoffFrequency (SampleType newCutoffFrequencyHz);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the type of the filter. */
|
||||
Type getType() const noexcept { return filterType; }
|
||||
|
||||
/** Returns the cutoff frequency of the filter. */
|
||||
SampleType getCutoffFrequency() const noexcept { return cutoffFrequency; }
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the filter. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the filter. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() <= s1.size());
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock.getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
|
||||
}
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Performs the filter operation on a single sample at a time. */
|
||||
SampleType processSample (int channel, SampleType inputValue);
|
||||
|
||||
/** Performs the filter operation on a single sample at a time, and returns both
|
||||
the low-pass and the high-pass outputs of the TPT structure.
|
||||
*/
|
||||
void processSample (int channel, SampleType inputValue, SampleType &outputLow, SampleType &outputHigh);
|
||||
|
||||
/** Ensure that the state variables are rounded to zero if the state
|
||||
variables are denormals. This is only needed if you are doing
|
||||
sample by sample processing.
|
||||
*/
|
||||
void snapToZero() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
SampleType g, R2, h;
|
||||
std::vector<SampleType> s1, s2, s3, s4;
|
||||
|
||||
double sampleRate = 44100.0;
|
||||
SampleType cutoffFrequency = 2000.0;
|
||||
Type filterType = Type::lowpass;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
770
deps/juce/modules/juce_dsp/processors/juce_Oversampling.cpp
vendored
Normal file
770
deps/juce/modules/juce_dsp/processors/juce_Oversampling.cpp
vendored
Normal file
@@ -0,0 +1,770 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** Abstract class for the provided oversampling stages used internally in
|
||||
the Oversampling class.
|
||||
*/
|
||||
template <typename SampleType>
|
||||
struct Oversampling<SampleType>::OversamplingStage
|
||||
{
|
||||
OversamplingStage (size_t numChans, size_t newFactor) : numChannels (numChans), factor (newFactor) {}
|
||||
virtual ~OversamplingStage() {}
|
||||
|
||||
//==============================================================================
|
||||
virtual SampleType getLatencyInSamples() const = 0;
|
||||
|
||||
virtual void initProcessing (size_t maximumNumberOfSamplesBeforeOversampling)
|
||||
{
|
||||
buffer.setSize (static_cast<int> (numChannels),
|
||||
static_cast<int> (maximumNumberOfSamplesBeforeOversampling * factor),
|
||||
false, false, true);
|
||||
}
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
AudioBlock<SampleType> getProcessedSamples (size_t numSamples)
|
||||
{
|
||||
return AudioBlock<SampleType> (buffer).getSubBlock (0, numSamples);
|
||||
}
|
||||
|
||||
virtual void processSamplesUp (const AudioBlock<const SampleType>&) = 0;
|
||||
virtual void processSamplesDown (AudioBlock<SampleType>&) = 0;
|
||||
|
||||
AudioBuffer<SampleType> buffer;
|
||||
size_t numChannels, factor;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Dummy oversampling stage class which simply copies and pastes the input
|
||||
signal, which could be equivalent to a "one time" oversampling processing.
|
||||
*/
|
||||
template <typename SampleType>
|
||||
struct OversamplingDummy : public Oversampling<SampleType>::OversamplingStage
|
||||
{
|
||||
using ParentType = typename Oversampling<SampleType>::OversamplingStage;
|
||||
|
||||
OversamplingDummy (size_t numChans) : ParentType (numChans, 1) {}
|
||||
|
||||
//==============================================================================
|
||||
SampleType getLatencyInSamples() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void processSamplesUp (const AudioBlock<const SampleType>& inputBlock) override
|
||||
{
|
||||
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (ParentType::buffer.getNumChannels()));
|
||||
jassert (inputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));
|
||||
|
||||
for (size_t channel = 0; channel < inputBlock.getNumChannels(); ++channel)
|
||||
ParentType::buffer.copyFrom (static_cast<int> (channel), 0,
|
||||
inputBlock.getChannelPointer (channel), static_cast<int> (inputBlock.getNumSamples()));
|
||||
}
|
||||
|
||||
void processSamplesDown (AudioBlock<SampleType>& outputBlock) override
|
||||
{
|
||||
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (ParentType::buffer.getNumChannels()));
|
||||
jassert (outputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));
|
||||
|
||||
outputBlock.copyFrom (ParentType::getProcessedSamples (outputBlock.getNumSamples()));
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OversamplingDummy)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Oversampling stage class performing 2 times oversampling using the Filter
|
||||
Design FIR Equiripple method. The resulting filter is linear phase,
|
||||
symmetric, and has every two samples but the middle one equal to zero,
|
||||
leading to specific processing optimizations.
|
||||
*/
|
||||
template <typename SampleType>
|
||||
struct Oversampling2TimesEquirippleFIR : public Oversampling<SampleType>::OversamplingStage
|
||||
{
|
||||
using ParentType = typename Oversampling<SampleType>::OversamplingStage;
|
||||
|
||||
Oversampling2TimesEquirippleFIR (size_t numChans,
|
||||
SampleType normalisedTransitionWidthUp,
|
||||
SampleType stopbandAmplitudedBUp,
|
||||
SampleType normalisedTransitionWidthDown,
|
||||
SampleType stopbandAmplitudedBDown)
|
||||
: ParentType (numChans, 2)
|
||||
{
|
||||
coefficientsUp = *FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalisedTransitionWidthUp, stopbandAmplitudedBUp);
|
||||
coefficientsDown = *FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalisedTransitionWidthDown, stopbandAmplitudedBDown);
|
||||
|
||||
auto N = coefficientsUp.getFilterOrder() + 1;
|
||||
stateUp.setSize (static_cast<int> (this->numChannels), static_cast<int> (N));
|
||||
|
||||
N = coefficientsDown.getFilterOrder() + 1;
|
||||
auto Ndiv2 = N / 2;
|
||||
auto Ndiv4 = Ndiv2 / 2;
|
||||
|
||||
stateDown.setSize (static_cast<int> (this->numChannels), static_cast<int> (N));
|
||||
stateDown2.setSize (static_cast<int> (this->numChannels), static_cast<int> (Ndiv4 + 1));
|
||||
|
||||
position.resize (static_cast<int> (this->numChannels));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SampleType getLatencyInSamples() const override
|
||||
{
|
||||
return static_cast<SampleType> (coefficientsUp.getFilterOrder() + coefficientsDown.getFilterOrder()) * 0.5f;
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
ParentType::reset();
|
||||
|
||||
stateUp.clear();
|
||||
stateDown.clear();
|
||||
stateDown2.clear();
|
||||
|
||||
position.fill (0);
|
||||
}
|
||||
|
||||
void processSamplesUp (const AudioBlock<const SampleType>& inputBlock) override
|
||||
{
|
||||
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (ParentType::buffer.getNumChannels()));
|
||||
jassert (inputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));
|
||||
|
||||
// Initialization
|
||||
auto fir = coefficientsUp.getRawCoefficients();
|
||||
auto N = coefficientsUp.getFilterOrder() + 1;
|
||||
auto Ndiv2 = N / 2;
|
||||
auto numSamples = inputBlock.getNumSamples();
|
||||
|
||||
// Processing
|
||||
for (size_t channel = 0; channel < inputBlock.getNumChannels(); ++channel)
|
||||
{
|
||||
auto bufferSamples = ParentType::buffer.getWritePointer (static_cast<int> (channel));
|
||||
auto buf = stateUp.getWritePointer (static_cast<int> (channel));
|
||||
auto samples = inputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
// Input
|
||||
buf[N - 1] = 2 * samples[i];
|
||||
|
||||
// Convolution
|
||||
auto out = static_cast<SampleType> (0.0);
|
||||
|
||||
for (size_t k = 0; k < Ndiv2; k += 2)
|
||||
out += (buf[k] + buf[N - k - 1]) * fir[k];
|
||||
|
||||
// Outputs
|
||||
bufferSamples[i << 1] = out;
|
||||
bufferSamples[(i << 1) + 1] = buf[Ndiv2 + 1] * fir[Ndiv2];
|
||||
|
||||
// Shift data
|
||||
for (size_t k = 0; k < N - 2; k += 2)
|
||||
buf[k] = buf[k + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processSamplesDown (AudioBlock<SampleType>& outputBlock) override
|
||||
{
|
||||
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (ParentType::buffer.getNumChannels()));
|
||||
jassert (outputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));
|
||||
|
||||
// Initialization
|
||||
auto fir = coefficientsDown.getRawCoefficients();
|
||||
auto N = coefficientsDown.getFilterOrder() + 1;
|
||||
auto Ndiv2 = N / 2;
|
||||
auto Ndiv4 = Ndiv2 / 2;
|
||||
auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
// Processing
|
||||
for (size_t channel = 0; channel < outputBlock.getNumChannels(); ++channel)
|
||||
{
|
||||
auto bufferSamples = ParentType::buffer.getWritePointer (static_cast<int> (channel));
|
||||
auto buf = stateDown.getWritePointer (static_cast<int> (channel));
|
||||
auto buf2 = stateDown2.getWritePointer (static_cast<int> (channel));
|
||||
auto samples = outputBlock.getChannelPointer (channel);
|
||||
auto pos = position.getUnchecked (static_cast<int> (channel));
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
// Input
|
||||
buf[N - 1] = bufferSamples[i << 1];
|
||||
|
||||
// Convolution
|
||||
auto out = static_cast<SampleType> (0.0);
|
||||
|
||||
for (size_t k = 0; k < Ndiv2; k += 2)
|
||||
out += (buf[k] + buf[N - k - 1]) * fir[k];
|
||||
|
||||
// Output
|
||||
out += buf2[pos] * fir[Ndiv2];
|
||||
buf2[pos] = bufferSamples[(i << 1) + 1];
|
||||
|
||||
samples[i] = out;
|
||||
|
||||
// Shift data
|
||||
for (size_t k = 0; k < N - 2; ++k)
|
||||
buf[k] = buf[k + 2];
|
||||
|
||||
// Circular buffer
|
||||
pos = (pos == 0 ? Ndiv4 : pos - 1);
|
||||
}
|
||||
|
||||
position.setUnchecked (static_cast<int> (channel), pos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FIR::Coefficients<SampleType> coefficientsUp, coefficientsDown;
|
||||
AudioBuffer<SampleType> stateUp, stateDown, stateDown2;
|
||||
Array<size_t> position;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesEquirippleFIR)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Oversampling stage class performing 2 times oversampling using the Filter
|
||||
Design IIR Polyphase Allpass Cascaded method. The resulting filter is minimum
|
||||
phase, and provided with a method to get the exact resulting latency.
|
||||
*/
|
||||
template <typename SampleType>
|
||||
struct Oversampling2TimesPolyphaseIIR : public Oversampling<SampleType>::OversamplingStage
|
||||
{
|
||||
using ParentType = typename Oversampling<SampleType>::OversamplingStage;
|
||||
|
||||
Oversampling2TimesPolyphaseIIR (size_t numChans,
|
||||
SampleType normalisedTransitionWidthUp,
|
||||
SampleType stopbandAmplitudedBUp,
|
||||
SampleType normalisedTransitionWidthDown,
|
||||
SampleType stopbandAmplitudedBDown)
|
||||
: ParentType (numChans, 2)
|
||||
{
|
||||
auto structureUp = FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalisedTransitionWidthUp, stopbandAmplitudedBUp);
|
||||
auto coeffsUp = getCoefficients (structureUp);
|
||||
latency = static_cast<SampleType> (-(coeffsUp.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * MathConstants<double>::twoPi));
|
||||
|
||||
auto structureDown = FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalisedTransitionWidthDown, stopbandAmplitudedBDown);
|
||||
auto coeffsDown = getCoefficients (structureDown);
|
||||
latency += static_cast<SampleType> (-(coeffsDown.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * MathConstants<double>::twoPi));
|
||||
|
||||
for (auto i = 0; i < structureUp.directPath.size(); ++i)
|
||||
coefficientsUp.add (structureUp.directPath.getObjectPointer (i)->coefficients[0]);
|
||||
|
||||
for (auto i = 1; i < structureUp.delayedPath.size(); ++i)
|
||||
coefficientsUp.add (structureUp.delayedPath.getObjectPointer (i)->coefficients[0]);
|
||||
|
||||
for (auto i = 0; i < structureDown.directPath.size(); ++i)
|
||||
coefficientsDown.add (structureDown.directPath.getObjectPointer (i)->coefficients[0]);
|
||||
|
||||
for (auto i = 1; i < structureDown.delayedPath.size(); ++i)
|
||||
coefficientsDown.add (structureDown.delayedPath.getObjectPointer (i)->coefficients[0]);
|
||||
|
||||
v1Up.setSize (static_cast<int> (this->numChannels), coefficientsUp.size());
|
||||
v1Down.setSize (static_cast<int> (this->numChannels), coefficientsDown.size());
|
||||
delayDown.resize (static_cast<int> (this->numChannels));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SampleType getLatencyInSamples() const override
|
||||
{
|
||||
return latency;
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
ParentType::reset();
|
||||
v1Up.clear();
|
||||
v1Down.clear();
|
||||
delayDown.fill (0);
|
||||
}
|
||||
|
||||
void processSamplesUp (const AudioBlock<const SampleType>& inputBlock) override
|
||||
{
|
||||
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (ParentType::buffer.getNumChannels()));
|
||||
jassert (inputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));
|
||||
|
||||
// Initialization
|
||||
auto coeffs = coefficientsUp.getRawDataPointer();
|
||||
auto numStages = coefficientsUp.size();
|
||||
auto delayedStages = numStages / 2;
|
||||
auto directStages = numStages - delayedStages;
|
||||
auto numSamples = inputBlock.getNumSamples();
|
||||
|
||||
// Processing
|
||||
for (size_t channel = 0; channel < inputBlock.getNumChannels(); ++channel)
|
||||
{
|
||||
auto bufferSamples = ParentType::buffer.getWritePointer (static_cast<int> (channel));
|
||||
auto lv1 = v1Up.getWritePointer (static_cast<int> (channel));
|
||||
auto samples = inputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
// Direct path cascaded allpass filters
|
||||
auto input = samples[i];
|
||||
|
||||
for (auto n = 0; n < directStages; ++n)
|
||||
{
|
||||
auto alpha = coeffs[n];
|
||||
auto output = alpha * input + lv1[n];
|
||||
lv1[n] = input - alpha * output;
|
||||
input = output;
|
||||
}
|
||||
|
||||
// Output
|
||||
bufferSamples[i << 1] = input;
|
||||
|
||||
// Delayed path cascaded allpass filters
|
||||
input = samples[i];
|
||||
|
||||
for (auto n = directStages; n < numStages; ++n)
|
||||
{
|
||||
auto alpha = coeffs[n];
|
||||
auto output = alpha * input + lv1[n];
|
||||
lv1[n] = input - alpha * output;
|
||||
input = output;
|
||||
}
|
||||
|
||||
// Output
|
||||
bufferSamples[(i << 1) + 1] = input;
|
||||
}
|
||||
}
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero (true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void processSamplesDown (AudioBlock<SampleType>& outputBlock) override
|
||||
{
|
||||
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (ParentType::buffer.getNumChannels()));
|
||||
jassert (outputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));
|
||||
|
||||
// Initialization
|
||||
auto coeffs = coefficientsDown.getRawDataPointer();
|
||||
auto numStages = coefficientsDown.size();
|
||||
auto delayedStages = numStages / 2;
|
||||
auto directStages = numStages - delayedStages;
|
||||
auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
// Processing
|
||||
for (size_t channel = 0; channel < outputBlock.getNumChannels(); ++channel)
|
||||
{
|
||||
auto bufferSamples = ParentType::buffer.getWritePointer (static_cast<int> (channel));
|
||||
auto lv1 = v1Down.getWritePointer (static_cast<int> (channel));
|
||||
auto samples = outputBlock.getChannelPointer (channel);
|
||||
auto delay = delayDown.getUnchecked (static_cast<int> (channel));
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
// Direct path cascaded allpass filters
|
||||
auto input = bufferSamples[i << 1];
|
||||
|
||||
for (auto n = 0; n < directStages; ++n)
|
||||
{
|
||||
auto alpha = coeffs[n];
|
||||
auto output = alpha * input + lv1[n];
|
||||
lv1[n] = input - alpha * output;
|
||||
input = output;
|
||||
}
|
||||
|
||||
auto directOut = input;
|
||||
|
||||
// Delayed path cascaded allpass filters
|
||||
input = bufferSamples[(i << 1) + 1];
|
||||
|
||||
for (auto n = directStages; n < numStages; ++n)
|
||||
{
|
||||
auto alpha = coeffs[n];
|
||||
auto output = alpha * input + lv1[n];
|
||||
lv1[n] = input - alpha * output;
|
||||
input = output;
|
||||
}
|
||||
|
||||
// Output
|
||||
samples[i] = (delay + directOut) * static_cast<SampleType> (0.5);
|
||||
delay = input;
|
||||
}
|
||||
|
||||
delayDown.setUnchecked (static_cast<int> (channel), delay);
|
||||
}
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero (false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void snapToZero (bool snapUpProcessing)
|
||||
{
|
||||
if (snapUpProcessing)
|
||||
{
|
||||
for (auto channel = 0; channel < ParentType::buffer.getNumChannels(); ++channel)
|
||||
{
|
||||
auto lv1 = v1Up.getWritePointer (channel);
|
||||
auto numStages = coefficientsUp.size();
|
||||
|
||||
for (auto n = 0; n < numStages; ++n)
|
||||
util::snapToZero (lv1[n]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto channel = 0; channel < ParentType::buffer.getNumChannels(); ++channel)
|
||||
{
|
||||
auto lv1 = v1Down.getWritePointer (channel);
|
||||
auto numStages = coefficientsDown.size();
|
||||
|
||||
for (auto n = 0; n < numStages; ++n)
|
||||
util::snapToZero (lv1[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
/** This function calculates the equivalent high order IIR filter of a given
|
||||
polyphase cascaded allpass filters structure.
|
||||
*/
|
||||
IIR::Coefficients<SampleType> getCoefficients (typename FilterDesign<SampleType>::IIRPolyphaseAllpassStructure& structure) const
|
||||
{
|
||||
constexpr auto one = static_cast<SampleType> (1.0);
|
||||
|
||||
Polynomial<SampleType> numerator1 ({ one }), denominator1 ({ one }),
|
||||
numerator2 ({ one }), denominator2 ({ one });
|
||||
|
||||
for (auto* i : structure.directPath)
|
||||
{
|
||||
auto coeffs = i->getRawCoefficients();
|
||||
|
||||
if (i->getFilterOrder() == 1)
|
||||
{
|
||||
numerator1 = numerator1 .getProductWith (Polynomial<SampleType> ({ coeffs[0], coeffs[1] }));
|
||||
denominator1 = denominator1.getProductWith (Polynomial<SampleType> ({ one, coeffs[2] }));
|
||||
}
|
||||
else
|
||||
{
|
||||
numerator1 = numerator1 .getProductWith (Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] }));
|
||||
denominator1 = denominator1.getProductWith (Polynomial<SampleType> ({ one, coeffs[3], coeffs[4] }));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* i : structure.delayedPath)
|
||||
{
|
||||
auto coeffs = i->getRawCoefficients();
|
||||
|
||||
if (i->getFilterOrder() == 1)
|
||||
{
|
||||
numerator2 = numerator2 .getProductWith (Polynomial<SampleType> ({ coeffs[0], coeffs[1] }));
|
||||
denominator2 = denominator2.getProductWith (Polynomial<SampleType> ({ one, coeffs[2] }));
|
||||
}
|
||||
else
|
||||
{
|
||||
numerator2 = numerator2 .getProductWith (Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] }));
|
||||
denominator2 = denominator2.getProductWith (Polynomial<SampleType> ({ one, coeffs[3], coeffs[4] }));
|
||||
}
|
||||
}
|
||||
|
||||
auto numeratorf1 = numerator1.getProductWith (denominator2);
|
||||
auto numeratorf2 = numerator2.getProductWith (denominator1);
|
||||
auto numerator = numeratorf1.getSumWith (numeratorf2);
|
||||
auto denominator = denominator1.getProductWith (denominator2);
|
||||
|
||||
IIR::Coefficients<SampleType> coeffs;
|
||||
|
||||
coeffs.coefficients.clear();
|
||||
auto inversion = one / denominator[0];
|
||||
|
||||
for (int i = 0; i <= numerator.getOrder(); ++i)
|
||||
coeffs.coefficients.add (numerator[i] * inversion);
|
||||
|
||||
for (int i = 1; i <= denominator.getOrder(); ++i)
|
||||
coeffs.coefficients.add (denominator[i] * inversion);
|
||||
|
||||
return coeffs;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<SampleType> coefficientsUp, coefficientsDown;
|
||||
SampleType latency;
|
||||
|
||||
AudioBuffer<SampleType> v1Up, v1Down;
|
||||
Array<SampleType> delayDown;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesPolyphaseIIR)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
Oversampling<SampleType>::Oversampling (size_t newNumChannels)
|
||||
: numChannels (newNumChannels)
|
||||
{
|
||||
jassert (numChannels > 0);
|
||||
|
||||
addDummyOversamplingStage();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
Oversampling<SampleType>::Oversampling (size_t newNumChannels, size_t newFactor,
|
||||
FilterType newType, bool isMaximumQuality,
|
||||
bool useIntegerLatency)
|
||||
: numChannels (newNumChannels), shouldUseIntegerLatency (useIntegerLatency)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newFactor, 5) && numChannels > 0);
|
||||
|
||||
if (newFactor == 0)
|
||||
{
|
||||
addDummyOversamplingStage();
|
||||
}
|
||||
else if (newType == FilterType::filterHalfBandPolyphaseIIR)
|
||||
{
|
||||
for (size_t n = 0; n < newFactor; ++n)
|
||||
{
|
||||
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.0f);
|
||||
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.0f);
|
||||
|
||||
auto gaindBStartUp = (isMaximumQuality ? -90.0f : -70.0f);
|
||||
auto gaindBStartDown = (isMaximumQuality ? -75.0f : -60.0f);
|
||||
auto gaindBFactorUp = (isMaximumQuality ? 10.0f : 8.0f);
|
||||
auto gaindBFactorDown = (isMaximumQuality ? 10.0f : 8.0f);
|
||||
|
||||
addOversamplingStage (FilterType::filterHalfBandPolyphaseIIR,
|
||||
twUp, gaindBStartUp + gaindBFactorUp * (float) n,
|
||||
twDown, gaindBStartDown + gaindBFactorDown * (float) n);
|
||||
}
|
||||
}
|
||||
else if (newType == FilterType::filterHalfBandFIREquiripple)
|
||||
{
|
||||
for (size_t n = 0; n < newFactor; ++n)
|
||||
{
|
||||
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.0f);
|
||||
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.0f);
|
||||
|
||||
auto gaindBStartUp = (isMaximumQuality ? -90.0f : -70.0f);
|
||||
auto gaindBStartDown = (isMaximumQuality ? -75.0f : -60.0f);
|
||||
auto gaindBFactorUp = (isMaximumQuality ? 10.0f : 8.0f);
|
||||
auto gaindBFactorDown = (isMaximumQuality ? 10.0f : 8.0f);
|
||||
|
||||
addOversamplingStage (FilterType::filterHalfBandFIREquiripple,
|
||||
twUp, gaindBStartUp + gaindBFactorUp * (float) n,
|
||||
twDown, gaindBStartDown + gaindBFactorDown * (float) n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
Oversampling<SampleType>::~Oversampling()
|
||||
{
|
||||
stages.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::addDummyOversamplingStage()
|
||||
{
|
||||
stages.add (new OversamplingDummy<SampleType> (numChannels));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::addOversamplingStage (FilterType type,
|
||||
float normalisedTransitionWidthUp,
|
||||
float stopbandAmplitudedBUp,
|
||||
float normalisedTransitionWidthDown,
|
||||
float stopbandAmplitudedBDown)
|
||||
{
|
||||
if (type == FilterType::filterHalfBandPolyphaseIIR)
|
||||
{
|
||||
stages.add (new Oversampling2TimesPolyphaseIIR<SampleType> (numChannels,
|
||||
normalisedTransitionWidthUp, stopbandAmplitudedBUp,
|
||||
normalisedTransitionWidthDown, stopbandAmplitudedBDown));
|
||||
}
|
||||
else
|
||||
{
|
||||
stages.add (new Oversampling2TimesEquirippleFIR<SampleType> (numChannels,
|
||||
normalisedTransitionWidthUp, stopbandAmplitudedBUp,
|
||||
normalisedTransitionWidthDown, stopbandAmplitudedBDown));
|
||||
}
|
||||
|
||||
factorOversampling *= 2;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::clearOversamplingStages()
|
||||
{
|
||||
stages.clear();
|
||||
factorOversampling = 1u;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::setUsingIntegerLatency (bool useIntegerLatency) noexcept
|
||||
{
|
||||
shouldUseIntegerLatency = useIntegerLatency;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
SampleType Oversampling<SampleType>::getLatencyInSamples() const noexcept
|
||||
{
|
||||
auto latency = getUncompensatedLatency();
|
||||
return shouldUseIntegerLatency ? latency + fractionalDelay : latency;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
SampleType Oversampling<SampleType>::getUncompensatedLatency() const noexcept
|
||||
{
|
||||
auto latency = static_cast<SampleType> (0);
|
||||
size_t order = 1;
|
||||
|
||||
for (auto* stage : stages)
|
||||
{
|
||||
order *= stage->factor;
|
||||
latency += stage->getLatencyInSamples() / static_cast<SampleType> (order);
|
||||
}
|
||||
|
||||
return latency;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
size_t Oversampling<SampleType>::getOversamplingFactor() const noexcept
|
||||
{
|
||||
return factorOversampling;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::initProcessing (size_t maximumNumberOfSamplesBeforeOversampling)
|
||||
{
|
||||
jassert (! stages.isEmpty());
|
||||
auto currentNumSamples = maximumNumberOfSamplesBeforeOversampling;
|
||||
|
||||
for (auto* stage : stages)
|
||||
{
|
||||
stage->initProcessing (currentNumSamples);
|
||||
currentNumSamples *= stage->factor;
|
||||
}
|
||||
|
||||
ProcessSpec spec = { 0.0, (uint32) maximumNumberOfSamplesBeforeOversampling, (uint32) numChannels };
|
||||
delay.prepare (spec);
|
||||
updateDelayLine();
|
||||
|
||||
isReady = true;
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::reset() noexcept
|
||||
{
|
||||
jassert (! stages.isEmpty());
|
||||
|
||||
if (isReady)
|
||||
for (auto* stage : stages)
|
||||
stage->reset();
|
||||
|
||||
delay.reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
AudioBlock<SampleType> Oversampling<SampleType>::processSamplesUp (const AudioBlock<const SampleType>& inputBlock) noexcept
|
||||
{
|
||||
jassert (! stages.isEmpty());
|
||||
|
||||
if (! isReady)
|
||||
return {};
|
||||
|
||||
auto* firstStage = stages.getUnchecked (0);
|
||||
firstStage->processSamplesUp (inputBlock);
|
||||
auto block = firstStage->getProcessedSamples (inputBlock.getNumSamples() * firstStage->factor);
|
||||
|
||||
for (int i = 1; i < stages.size(); ++i)
|
||||
{
|
||||
stages[i]->processSamplesUp (block);
|
||||
block = stages[i]->getProcessedSamples (block.getNumSamples() * stages[i]->factor);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::processSamplesDown (AudioBlock<SampleType>& outputBlock) noexcept
|
||||
{
|
||||
jassert (! stages.isEmpty());
|
||||
|
||||
if (! isReady)
|
||||
return;
|
||||
|
||||
auto currentNumSamples = outputBlock.getNumSamples();
|
||||
|
||||
for (int n = 0; n < stages.size() - 1; ++n)
|
||||
currentNumSamples *= stages.getUnchecked(n)->factor;
|
||||
|
||||
for (int n = stages.size() - 1; n > 0; --n)
|
||||
{
|
||||
auto& stage = *stages.getUnchecked(n);
|
||||
auto audioBlock = stages.getUnchecked (n - 1)->getProcessedSamples (currentNumSamples);
|
||||
stage.processSamplesDown (audioBlock);
|
||||
|
||||
currentNumSamples /= stage.factor;
|
||||
}
|
||||
|
||||
stages.getFirst()->processSamplesDown (outputBlock);
|
||||
|
||||
if (shouldUseIntegerLatency && fractionalDelay > static_cast<SampleType> (0.0))
|
||||
{
|
||||
auto context = ProcessContextReplacing<SampleType> (outputBlock);
|
||||
delay.process (context);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Oversampling<SampleType>::updateDelayLine()
|
||||
{
|
||||
auto latency = getUncompensatedLatency();
|
||||
fractionalDelay = static_cast<SampleType> (1.0) - (latency - std::floor (latency));
|
||||
|
||||
if (fractionalDelay == static_cast<SampleType> (1.0))
|
||||
fractionalDelay = static_cast<SampleType> (0.0);
|
||||
else if (fractionalDelay < static_cast<SampleType> (0.618))
|
||||
fractionalDelay += static_cast<SampleType> (1.0);
|
||||
|
||||
delay.setDelay (fractionalDelay);
|
||||
}
|
||||
|
||||
template class Oversampling<float>;
|
||||
template class Oversampling<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
214
deps/juce/modules/juce_dsp/processors/juce_Oversampling.h
vendored
Normal file
214
deps/juce/modules/juce_dsp/processors/juce_Oversampling.h
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//===============================================================================
|
||||
/**
|
||||
A processor that performs multi-channel oversampling.
|
||||
|
||||
This class can be configured to do a factor of 2, 4, 8 or 16 times
|
||||
oversampling, using multiple stages, with polyphase allpass IIR filters or FIR
|
||||
filters, and latency compensation.
|
||||
|
||||
The principle of oversampling is to increase the sample rate of a given
|
||||
non-linear process to prevent it from creating aliasing. Oversampling works
|
||||
by upsampling the input signal N times, processing the upsampled signal
|
||||
with the increased internal sample rate, then downsampling the result to get
|
||||
back to the original sample rate.
|
||||
|
||||
Choose between FIR or IIR filtering depending on your needs in terms of
|
||||
latency and phase distortion. With FIR filters the phase is linear but the
|
||||
latency is maximised. With IIR filtering the phase is compromised around the
|
||||
Nyquist frequency but the latency is minimised.
|
||||
|
||||
@see FilterDesign.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class JUCE_API Oversampling
|
||||
{
|
||||
public:
|
||||
/** The type of filter that can be used for the oversampling processing. */
|
||||
enum FilterType
|
||||
{
|
||||
filterHalfBandFIREquiripple = 0,
|
||||
filterHalfBandPolyphaseIIR,
|
||||
numFilterTypes
|
||||
};
|
||||
|
||||
//===============================================================================
|
||||
/** The default constructor.
|
||||
|
||||
Note: This creates a "dummy" oversampling stage, which needs to be removed
|
||||
before adding proper oversampling stages.
|
||||
|
||||
@param numChannels the number of channels to process with this object
|
||||
|
||||
@see clearOversamplingStages, addOversamplingStage
|
||||
*/
|
||||
explicit Oversampling (size_t numChannels = 1);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param numChannels the number of channels to process with this object
|
||||
@param factor the processing will perform 2 ^ factor times oversampling
|
||||
@param type the type of filter design employed for filtering during
|
||||
oversampling
|
||||
@param isMaxQuality if the oversampling is done using the maximum quality, where
|
||||
the filters will be more efficient but the CPU load will
|
||||
increase as well
|
||||
@param useIntegerLatency if true this processor will add some fractional delay at the
|
||||
end of the signal path to ensure that the overall latency of
|
||||
the oversampling is an integer
|
||||
*/
|
||||
Oversampling (size_t numChannels,
|
||||
size_t factor,
|
||||
FilterType type,
|
||||
bool isMaxQuality = true,
|
||||
bool useIntegerLatency = false);
|
||||
|
||||
/** Destructor. */
|
||||
~Oversampling();
|
||||
|
||||
//===============================================================================
|
||||
/* Sets if this processor should add some fractional delay at the end of the signal
|
||||
path to ensure that the overall latency of the oversampling is an integer.
|
||||
*/
|
||||
void setUsingIntegerLatency (bool shouldUseIntegerLatency) noexcept;
|
||||
|
||||
/** Returns the latency in samples of the overall processing. You can use this
|
||||
information in your main processor to compensate the additional latency
|
||||
involved with the oversampling, for example with a dry / wet mixer, and to
|
||||
report the latency to the DAW.
|
||||
|
||||
Note: If you have not opted to use an integer latency then the latency may not be
|
||||
integer, so you might need to round its value or to compensate it properly in
|
||||
your processing code since plug-ins can only report integer latency values in
|
||||
samples to the DAW.
|
||||
*/
|
||||
SampleType getLatencyInSamples() const noexcept;
|
||||
|
||||
/** Returns the current oversampling factor. */
|
||||
size_t getOversamplingFactor() const noexcept;
|
||||
|
||||
//===============================================================================
|
||||
/** Must be called before any processing, to set the buffer sizes of the internal
|
||||
buffers of the oversampling processing.
|
||||
*/
|
||||
void initProcessing (size_t maximumNumberOfSamplesBeforeOversampling);
|
||||
|
||||
/** Resets the processing pipeline, ready to oversample a new stream of data. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Must be called to perform the upsampling, prior to any oversampled processing.
|
||||
|
||||
Returns an AudioBlock referencing the oversampled input signal, which must be
|
||||
used to perform the non-linear processing which needs the higher sample rate.
|
||||
Don't forget to set the sample rate of that processing to N times the original
|
||||
sample rate.
|
||||
*/
|
||||
AudioBlock<SampleType> processSamplesUp (const AudioBlock<const SampleType>& inputBlock) noexcept;
|
||||
|
||||
/** Must be called to perform the downsampling, after the upsampling and the
|
||||
non-linear processing. The output signal is probably delayed by the internal
|
||||
latency of the whole oversampling behaviour, so don't forget to take this
|
||||
into account.
|
||||
*/
|
||||
void processSamplesDown (AudioBlock<SampleType>& outputBlock) noexcept;
|
||||
|
||||
//===============================================================================
|
||||
/** Adds a new oversampling stage to the Oversampling class, multiplying the
|
||||
current oversampling factor by two. This is used with the default constructor
|
||||
to create custom oversampling chains, requiring a call to the
|
||||
clearOversamplingStages before any addition.
|
||||
|
||||
Note: Upsampling and downsampling filtering have different purposes, the
|
||||
former removes upsampling artefacts while the latter removes useless frequency
|
||||
content created by the oversampled process, so usually the attenuation is
|
||||
increased when upsampling compared to downsampling.
|
||||
|
||||
@param normalisedTransitionWidthUp a value between 0 and 0.5 which specifies how much
|
||||
the transition between passband and stopband is
|
||||
steep, for upsampling filtering (the lower the better)
|
||||
@param stopbandAmplitudedBUp the amplitude in dB in the stopband for upsampling
|
||||
filtering, must be negative
|
||||
@param normalisedTransitionWidthDown a value between 0 and 0.5 which specifies how much
|
||||
the transition between passband and stopband is
|
||||
steep, for downsampling filtering (the lower the better)
|
||||
@param stopbandAmplitudedBDown the amplitude in dB in the stopband for downsampling
|
||||
filtering, must be negative
|
||||
|
||||
@see clearOversamplingStages
|
||||
*/
|
||||
void addOversamplingStage (FilterType,
|
||||
float normalisedTransitionWidthUp, float stopbandAmplitudedBUp,
|
||||
float normalisedTransitionWidthDown, float stopbandAmplitudedBDown);
|
||||
|
||||
/** Adds a new "dummy" oversampling stage, which does nothing to the signal. Using
|
||||
one can be useful if your application features a customisable oversampling factor
|
||||
and if you want to select the current one from an OwnedArray without changing
|
||||
anything in the processing code.
|
||||
|
||||
@see OwnedArray, clearOversamplingStages, addOversamplingStage
|
||||
*/
|
||||
void addDummyOversamplingStage();
|
||||
|
||||
/** Removes all the previously registered oversampling stages, so you can add
|
||||
your own from scratch.
|
||||
|
||||
@see addOversamplingStage, addDummyOversamplingStage
|
||||
*/
|
||||
void clearOversamplingStages();
|
||||
|
||||
//===============================================================================
|
||||
size_t factorOversampling = 1;
|
||||
size_t numChannels = 1;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
struct OversamplingStage;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//===============================================================================
|
||||
void updateDelayLine();
|
||||
SampleType getUncompensatedLatency() const noexcept;
|
||||
|
||||
//===============================================================================
|
||||
OwnedArray<OversamplingStage> stages;
|
||||
bool isReady = false, shouldUseIntegerLatency = false;
|
||||
DelayLine<SampleType, DelayLineInterpolationTypes::Thiran> delay { 8 };
|
||||
SampleType fractionalDelay = 0;
|
||||
|
||||
//===============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling)
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
143
deps/juce/modules/juce_dsp/processors/juce_Panner.cpp
vendored
Normal file
143
deps/juce/modules/juce_dsp/processors/juce_Panner.cpp
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
Panner<SampleType>::Panner()
|
||||
{
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Panner<SampleType>::setRule (Rule newRule)
|
||||
{
|
||||
currentRule = newRule;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Panner<SampleType>::setPan (SampleType newPan)
|
||||
{
|
||||
jassert (newPan >= -1.0 && newPan <= 1.0);
|
||||
|
||||
pan = jlimit (static_cast<SampleType> (-1.0), static_cast<SampleType> (1.0), newPan);
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Panner<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Panner<SampleType>::reset()
|
||||
{
|
||||
leftVolume .reset (sampleRate, 0.05);
|
||||
rightVolume.reset (sampleRate, 0.05);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Panner<SampleType>::update()
|
||||
{
|
||||
SampleType leftValue, rightValue, boostValue;
|
||||
|
||||
auto normalisedPan = static_cast<SampleType> (0.5) * (pan + static_cast<SampleType> (1.0));
|
||||
|
||||
switch (currentRule)
|
||||
{
|
||||
case Rule::balanced:
|
||||
leftValue = jmin (static_cast<SampleType> (0.5), static_cast<SampleType> (1.0) - normalisedPan);
|
||||
rightValue = jmin (static_cast<SampleType> (0.5), normalisedPan);
|
||||
boostValue = static_cast<SampleType> (2.0);
|
||||
break;
|
||||
|
||||
case Rule::linear:
|
||||
leftValue = static_cast<SampleType> (1.0) - normalisedPan;
|
||||
rightValue = normalisedPan;
|
||||
boostValue = static_cast<SampleType> (2.0);
|
||||
break;
|
||||
|
||||
case Rule::sin3dB:
|
||||
leftValue = static_cast<SampleType> (std::sin (0.5 * MathConstants<double>::pi * (1.0 - normalisedPan)));
|
||||
rightValue = static_cast<SampleType> (std::sin (0.5 * MathConstants<double>::pi * normalisedPan));
|
||||
boostValue = std::sqrt (static_cast<SampleType> (2.0));
|
||||
break;
|
||||
|
||||
case Rule::sin4p5dB:
|
||||
leftValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * (1.0 - normalisedPan)), 1.5));
|
||||
rightValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * normalisedPan), 1.5));
|
||||
boostValue = static_cast<SampleType> (std::pow (2.0, 3.0 / 4.0));
|
||||
break;
|
||||
|
||||
case Rule::sin6dB:
|
||||
leftValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * (1.0 - normalisedPan)), 2.0));
|
||||
rightValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * normalisedPan), 2.0));
|
||||
boostValue = static_cast<SampleType> (2.0);
|
||||
break;
|
||||
|
||||
case Rule::squareRoot3dB:
|
||||
leftValue = std::sqrt (static_cast<SampleType> (1.0) - normalisedPan);
|
||||
rightValue = std::sqrt (normalisedPan);
|
||||
boostValue = std::sqrt (static_cast<SampleType> (2.0));
|
||||
break;
|
||||
|
||||
case Rule::squareRoot4p5dB:
|
||||
leftValue = static_cast<SampleType> (std::pow (std::sqrt (1.0 - normalisedPan), 1.5));
|
||||
rightValue = static_cast<SampleType> (std::pow (std::sqrt (normalisedPan), 1.5));
|
||||
boostValue = static_cast<SampleType> (std::pow (2.0, 3.0 / 4.0));
|
||||
break;
|
||||
|
||||
default:
|
||||
leftValue = jmin (static_cast<SampleType> (0.5), static_cast<SampleType> (1.0) - normalisedPan);
|
||||
rightValue = jmin (static_cast<SampleType> (0.5), normalisedPan);
|
||||
boostValue = static_cast<SampleType> (2.0);
|
||||
break;
|
||||
}
|
||||
|
||||
leftVolume .setTargetValue (leftValue * boostValue);
|
||||
rightVolume.setTargetValue (rightValue * boostValue);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class Panner<float>;
|
||||
template class Panner<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
121
deps/juce/modules/juce_dsp/processors/juce_Panner.h
vendored
Normal file
121
deps/juce/modules/juce_dsp/processors/juce_Panner.h
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class PannerRule
|
||||
{
|
||||
linear, // regular 6 dB or linear panning rule, allows the panned sound to be
|
||||
// perceived as having a constant level when summed to mono
|
||||
balanced, // both left and right are 1 when pan value is 0, with left decreasing
|
||||
// to 0 above this value and right decreasing to 0 below it
|
||||
sin3dB, // alternate version of the regular 3 dB panning rule with a sine curve
|
||||
sin4p5dB, // alternate version of the regular 4.5 dB panning rule with a sine curve
|
||||
sin6dB, // alternate version of the regular 6 dB panning rule with a sine curve
|
||||
squareRoot3dB, // regular 3 dB or constant power panning rule, allows the panned sound
|
||||
// to be perceived as having a constant level regardless of the pan position
|
||||
squareRoot4p5dB // regular 4.5 dB panning rule, a compromise option between 3 dB and 6 dB panning rules
|
||||
};
|
||||
|
||||
/**
|
||||
A processor to perform panning operations on stereo buffers.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Panner
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using Rule = PannerRule;
|
||||
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
Panner();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the panning rule. */
|
||||
void setRule (Rule newRule);
|
||||
|
||||
/** Sets the current panning value, between -1 (full left) and 1 (full right). */
|
||||
void setPan (SampleType newPan);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
|
||||
const auto numInputChannels = inputBlock.getNumChannels();
|
||||
const auto numOutputChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassertquiet (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (numOutputChannels != 2 || numInputChannels == 0 || numInputChannels > 2)
|
||||
return;
|
||||
|
||||
if (numInputChannels == 2)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputBlock.getSingleChannelBlock (0).copyFrom (inputBlock);
|
||||
outputBlock.getSingleChannelBlock (1).copyFrom (inputBlock);
|
||||
}
|
||||
|
||||
if (context.isBypassed)
|
||||
return;
|
||||
|
||||
outputBlock.getSingleChannelBlock (0).multiplyBy (leftVolume);
|
||||
outputBlock.getSingleChannelBlock (1).multiplyBy (rightVolume);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
Rule currentRule = Rule::balanced;
|
||||
SampleType pan = 0.0;
|
||||
SmoothedValue<SampleType> leftVolume, rightVolume;
|
||||
double sampleRate = 44100.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
177
deps/juce/modules/juce_dsp/processors/juce_ProcessContext.h
vendored
Normal file
177
deps/juce/modules/juce_dsp/processors/juce_ProcessContext.h
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
/**
|
||||
This structure is passed into a DSP algorithm's prepare() method, and contains
|
||||
information about various aspects of the context in which it can expect to be called.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct ProcessSpec
|
||||
{
|
||||
/** The sample rate that will be used for the data that is sent to the processor. */
|
||||
double sampleRate;
|
||||
|
||||
/** The maximum number of samples that will be in the blocks sent to process() method. */
|
||||
uint32 maximumBlockSize;
|
||||
|
||||
/** The number of channels that the process() method will be expected to handle. */
|
||||
uint32 numChannels;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This is a handy base class for the state of a processor (such as parameter values)
|
||||
which is typically shared among several processors. This is useful for multi-mono
|
||||
filters which share the same state among several mono processors.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct ProcessorState : public ReferenceCountedObject
|
||||
{
|
||||
/** The ProcessorState structure is ref-counted, so this is a handy type that can be used
|
||||
as a pointer to one.
|
||||
*/
|
||||
using Ptr = ReferenceCountedObjectPtr<ProcessorState>;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains context information that is passed into an algorithm's process method.
|
||||
|
||||
This context is intended for use in situations where a single block is being used
|
||||
for both the input and output, so it will return the same object for both its
|
||||
getInputBlock() and getOutputBlock() methods.
|
||||
|
||||
@see ProcessContextNonReplacing
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename ContextSampleType>
|
||||
struct ProcessContextReplacing
|
||||
{
|
||||
public:
|
||||
/** The type of a single sample (which may be a vector if multichannel). */
|
||||
using SampleType = ContextSampleType;
|
||||
/** The type of audio block that this context handles. */
|
||||
using AudioBlockType = AudioBlock<SampleType>;
|
||||
using ConstAudioBlockType = AudioBlock<const SampleType>;
|
||||
|
||||
/** Creates a ProcessContextReplacing that uses the given audio block.
|
||||
Note that the caller must not delete the block while it is still in use by this object!
|
||||
*/
|
||||
ProcessContextReplacing (AudioBlockType& block) noexcept : ioBlock (block) {}
|
||||
|
||||
ProcessContextReplacing (const ProcessContextReplacing&) = default;
|
||||
ProcessContextReplacing (ProcessContextReplacing&&) = default;
|
||||
|
||||
/** Returns the audio block to use as the input to a process function. */
|
||||
const ConstAudioBlockType& getInputBlock() const noexcept { return constBlock; }
|
||||
|
||||
/** Returns the audio block to use as the output to a process function. */
|
||||
AudioBlockType& getOutputBlock() const noexcept { return ioBlock; }
|
||||
|
||||
/** All process context classes will define this constant method so that templated
|
||||
code can determine whether the input and output blocks refer to the same buffer,
|
||||
or to two different ones.
|
||||
*/
|
||||
static constexpr bool usesSeparateInputAndOutputBlocks() { return false; }
|
||||
|
||||
/** If set to true, then a processor's process() method is expected to do whatever
|
||||
is appropriate for it to be in a bypassed state.
|
||||
*/
|
||||
bool isBypassed = false;
|
||||
|
||||
private:
|
||||
AudioBlockType& ioBlock;
|
||||
ConstAudioBlockType constBlock { ioBlock };
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains context information that is passed into an algorithm's process method.
|
||||
|
||||
This context is intended for use in situations where two different blocks are being
|
||||
used the input and output to the process algorithm, so the processor must read from
|
||||
the block returned by getInputBlock() and write its results to the block returned by
|
||||
getOutputBlock().
|
||||
|
||||
@see ProcessContextReplacing
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename ContextSampleType>
|
||||
struct ProcessContextNonReplacing
|
||||
{
|
||||
public:
|
||||
/** The type of a single sample (which may be a vector if multichannel). */
|
||||
using SampleType = ContextSampleType;
|
||||
/** The type of audio block that this context handles. */
|
||||
using AudioBlockType = AudioBlock<SampleType>;
|
||||
using ConstAudioBlockType = AudioBlock<const SampleType>;
|
||||
|
||||
/** Creates a ProcessContextReplacing that uses the given input and output blocks.
|
||||
Note that the caller must not delete these blocks while they are still in use by this object!
|
||||
*/
|
||||
ProcessContextNonReplacing (const ConstAudioBlockType& input, AudioBlockType& output) noexcept
|
||||
: inputBlock (input), outputBlock (output)
|
||||
{
|
||||
// If the input and output blocks are the same then you should use
|
||||
// ProcessContextReplacing instead.
|
||||
jassert (input != output);
|
||||
}
|
||||
|
||||
ProcessContextNonReplacing (const ProcessContextNonReplacing&) = default;
|
||||
ProcessContextNonReplacing (ProcessContextNonReplacing&&) = default;
|
||||
|
||||
/** Returns the audio block to use as the input to a process function. */
|
||||
const ConstAudioBlockType& getInputBlock() const noexcept { return inputBlock; }
|
||||
|
||||
/** Returns the audio block to use as the output to a process function. */
|
||||
AudioBlockType& getOutputBlock() const noexcept { return outputBlock; }
|
||||
|
||||
/** All process context classes will define this constant method so that templated
|
||||
code can determine whether the input and output blocks refer to the same buffer,
|
||||
or to two different ones.
|
||||
*/
|
||||
static constexpr bool usesSeparateInputAndOutputBlocks() { return true; }
|
||||
|
||||
/** If set to true, then a processor's process() method is expected to do whatever
|
||||
is appropriate for it to be in a bypassed state.
|
||||
*/
|
||||
bool isBypassed = false;
|
||||
|
||||
private:
|
||||
ConstAudioBlockType inputBlock;
|
||||
AudioBlockType& outputBlock;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
175
deps/juce/modules/juce_dsp/processors/juce_ProcessorChain.h
vendored
Normal file
175
deps/juce/modules/juce_dsp/processors/juce_ProcessorChain.h
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
/** The contents of this namespace are used to implement ProcessorChain and should
|
||||
not be used elsewhere. Their interfaces (and existence) are liable to change!
|
||||
*/
|
||||
namespace detail
|
||||
{
|
||||
template <typename Fn, typename Tuple, size_t... Ix>
|
||||
constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple, std::index_sequence<Ix...>)
|
||||
noexcept (noexcept (std::initializer_list<int> { (fn (std::get<Ix> (tuple), Ix), 0)... }))
|
||||
{
|
||||
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), Ix), 0)... };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using TupleIndexSequence = std::make_index_sequence<std::tuple_size<std::remove_cv_t<std::remove_reference_t<T>>>::value>;
|
||||
|
||||
template <typename Fn, typename Tuple>
|
||||
constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple)
|
||||
noexcept (noexcept (forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{})))
|
||||
{
|
||||
forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** This variadically-templated class lets you join together any number of processor
|
||||
classes into a single processor which will call process() on them all in sequence.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename... Processors>
|
||||
class ProcessorChain
|
||||
{
|
||||
public:
|
||||
/** Get a reference to the processor at index `Index`. */
|
||||
template <int Index> auto& get() noexcept { return std::get<Index> (processors); }
|
||||
|
||||
/** Get a reference to the processor at index `Index`. */
|
||||
template <int Index> const auto& get() const noexcept { return std::get<Index> (processors); }
|
||||
|
||||
/** Set the processor at index `Index` to be bypassed or enabled. */
|
||||
template <int Index>
|
||||
void setBypassed (bool b) noexcept { bypassed[(size_t) Index] = b; }
|
||||
|
||||
/** Query whether the processor at index `Index` is bypassed. */
|
||||
template <int Index>
|
||||
bool isBypassed() const noexcept { return bypassed[(size_t) Index]; }
|
||||
|
||||
/** Prepare all inner processors with the provided `ProcessSpec`. */
|
||||
void prepare (const ProcessSpec& spec)
|
||||
{
|
||||
detail::forEachInTuple ([&] (auto& proc, size_t) { proc.prepare (spec); }, processors);
|
||||
}
|
||||
|
||||
/** Reset all inner processors. */
|
||||
void reset()
|
||||
{
|
||||
detail::forEachInTuple ([] (auto& proc, size_t) { proc.reset(); }, processors);
|
||||
}
|
||||
|
||||
/** Process `context` through all inner processors in sequence. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
detail::forEachInTuple ([&] (auto& proc, size_t index) noexcept
|
||||
{
|
||||
if (context.usesSeparateInputAndOutputBlocks() && index != 0)
|
||||
{
|
||||
jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels());
|
||||
ProcessContextReplacing<typename ProcessContext::SampleType> replacingContext (context.getOutputBlock());
|
||||
replacingContext.isBypassed = (bypassed[index] || context.isBypassed);
|
||||
|
||||
proc.process (replacingContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessContext contextCopy (context);
|
||||
contextCopy.isBypassed = (bypassed[index] || context.isBypassed);
|
||||
|
||||
proc.process (contextCopy);
|
||||
}
|
||||
}, processors);
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Processors...> processors;
|
||||
std::array<bool, sizeof...(Processors)> bypassed { {} };
|
||||
};
|
||||
|
||||
/** Non-member equivalent of ProcessorChain::get which avoids awkward
|
||||
member template syntax.
|
||||
*/
|
||||
template <int Index, typename... Processors>
|
||||
inline auto& get (ProcessorChain<Processors...>& chain) noexcept
|
||||
{
|
||||
return chain.template get<Index>();
|
||||
}
|
||||
|
||||
/** Non-member equivalent of ProcessorChain::get which avoids awkward
|
||||
member template syntax.
|
||||
*/
|
||||
template <int Index, typename... Processors>
|
||||
inline auto& get (const ProcessorChain<Processors...>& chain) noexcept
|
||||
{
|
||||
return chain.template get<Index>();
|
||||
}
|
||||
|
||||
/** Non-member equivalent of ProcessorChain::setBypassed which avoids awkward
|
||||
member template syntax.
|
||||
*/
|
||||
template <int Index, typename... Processors>
|
||||
inline void setBypassed (ProcessorChain<Processors...>& chain, bool bypassed) noexcept
|
||||
{
|
||||
chain.template setBypassed<Index> (bypassed);
|
||||
}
|
||||
|
||||
/** Non-member equivalent of ProcessorChain::isBypassed which avoids awkward
|
||||
member template syntax.
|
||||
*/
|
||||
template <int Index, typename... Processors>
|
||||
inline bool isBypassed (const ProcessorChain<Processors...>& chain) noexcept
|
||||
{
|
||||
return chain.template isBypassed<Index>();
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmismatched-tags")
|
||||
|
||||
/** Adds support for C++17 structured bindings. */
|
||||
template <typename... Processors>
|
||||
struct tuple_size<::juce::dsp::ProcessorChain<Processors...>> : integral_constant<size_t, sizeof... (Processors)> {};
|
||||
|
||||
/** Adds support for C++17 structured bindings. */
|
||||
template <size_t I, typename... Processors>
|
||||
struct tuple_element<I, ::juce::dsp::ProcessorChain<Processors...>> : tuple_element<I, tuple<Processors...>> {};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace std
|
||||
141
deps/juce/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp
vendored
Normal file
141
deps/juce/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class ProcessorChainTest : public UnitTest
|
||||
{
|
||||
template <int AddValue>
|
||||
struct MockProcessor
|
||||
{
|
||||
void prepare (const ProcessSpec&) noexcept { isPrepared = true; }
|
||||
void reset() noexcept { isReset = true; }
|
||||
|
||||
template <typename Context>
|
||||
void process (const Context& context) noexcept
|
||||
{
|
||||
bufferWasClear = context.getInputBlock().getSample (0, 0) == 0;
|
||||
|
||||
if (! context.isBypassed)
|
||||
context.getOutputBlock().add (AddValue);
|
||||
}
|
||||
|
||||
bool isPrepared = false;
|
||||
bool isReset = false;
|
||||
bool bufferWasClear = false;
|
||||
};
|
||||
|
||||
public:
|
||||
ProcessorChainTest()
|
||||
: UnitTest ("ProcessorChain", UnitTestCategories::dsp) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("After calling setBypass, processor is bypassed");
|
||||
{
|
||||
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
|
||||
|
||||
setBypassed<0> (chain, true);
|
||||
expect (isBypassed<0> (chain));
|
||||
setBypassed<0> (chain, false);
|
||||
expect (! isBypassed<0> (chain));
|
||||
|
||||
setBypassed<1> (chain, true);
|
||||
expect (isBypassed<1> (chain));
|
||||
setBypassed<1> (chain, false);
|
||||
expect (! isBypassed<1> (chain));
|
||||
}
|
||||
|
||||
beginTest ("After calling prepare, all processors are prepared");
|
||||
{
|
||||
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
|
||||
|
||||
expect (! get<0> (chain).isPrepared);
|
||||
expect (! get<1> (chain).isPrepared);
|
||||
|
||||
chain.prepare (ProcessSpec{});
|
||||
|
||||
expect (get<0> (chain).isPrepared);
|
||||
expect (get<1> (chain).isPrepared);
|
||||
}
|
||||
|
||||
beginTest ("After calling reset, all processors are reset");
|
||||
{
|
||||
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
|
||||
|
||||
expect (! get<0> (chain).isReset);
|
||||
expect (! get<1> (chain).isReset);
|
||||
|
||||
chain.reset();
|
||||
|
||||
expect (get<0> (chain).isReset);
|
||||
expect (get<1> (chain).isReset);
|
||||
}
|
||||
|
||||
beginTest ("After calling process, all processors contribute to processing");
|
||||
{
|
||||
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
|
||||
|
||||
AudioBuffer<float> buffer (1, 1);
|
||||
AudioBlock<float> block (buffer);
|
||||
ProcessContextReplacing<float> context (block);
|
||||
|
||||
block.clear();
|
||||
chain.process (context);
|
||||
expectEquals (buffer.getSample (0, 0), 3.0f);
|
||||
expect (get<0> (chain).bufferWasClear);
|
||||
expect (! get<1> (chain).bufferWasClear);
|
||||
|
||||
setBypassed<0> (chain, true);
|
||||
block.clear();
|
||||
chain.process (context);
|
||||
expectEquals (buffer.getSample (0, 0), 2.0f);
|
||||
expect (get<0> (chain).bufferWasClear);
|
||||
expect (get<1> (chain).bufferWasClear);
|
||||
|
||||
setBypassed<1> (chain, true);
|
||||
block.clear();
|
||||
chain.process (context);
|
||||
expectEquals (buffer.getSample (0, 0), 0.0f);
|
||||
expect (get<0> (chain).bufferWasClear);
|
||||
expect (get<1> (chain).bufferWasClear);
|
||||
|
||||
setBypassed<0> (chain, false);
|
||||
block.clear();
|
||||
chain.process (context);
|
||||
expectEquals (buffer.getSample (0, 0), 1.0f);
|
||||
expect (get<0> (chain).bufferWasClear);
|
||||
expect (! get<1> (chain).bufferWasClear);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static ProcessorChainTest processorChainUnitTest;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
99
deps/juce/modules/juce_dsp/processors/juce_ProcessorDuplicator.h
vendored
Normal file
99
deps/juce/modules/juce_dsp/processors/juce_ProcessorDuplicator.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Converts a mono processor class into a multi-channel version by duplicating it
|
||||
and applying multichannel buffers across an array of instances.
|
||||
|
||||
When the prepare method is called, it uses the specified number of channels to
|
||||
instantiate the appropriate number of instances, which it then uses in its
|
||||
process() method.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename MonoProcessorType, typename StateType>
|
||||
struct ProcessorDuplicator
|
||||
{
|
||||
ProcessorDuplicator() : state (new StateType()) {}
|
||||
ProcessorDuplicator (StateType* stateToUse) : state (stateToUse) {}
|
||||
ProcessorDuplicator (typename StateType::Ptr stateToUse) : state (std::move (stateToUse)) {}
|
||||
ProcessorDuplicator (const ProcessorDuplicator&) = default;
|
||||
ProcessorDuplicator (ProcessorDuplicator&&) = default;
|
||||
|
||||
void prepare (const ProcessSpec& spec)
|
||||
{
|
||||
processors.removeRange ((int) spec.numChannels, processors.size());
|
||||
|
||||
while (static_cast<size_t> (processors.size()) < spec.numChannels)
|
||||
processors.add (new MonoProcessorType (state));
|
||||
|
||||
auto monoSpec = spec;
|
||||
monoSpec.numChannels = 1;
|
||||
|
||||
for (auto* p : processors)
|
||||
p->prepare (monoSpec);
|
||||
}
|
||||
|
||||
void reset() noexcept { for (auto* p : processors) p->reset(); }
|
||||
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
jassert ((int) context.getInputBlock().getNumChannels() <= processors.size());
|
||||
jassert ((int) context.getOutputBlock().getNumChannels() <= processors.size());
|
||||
|
||||
auto numChannels = static_cast<size_t> (jmin (context.getInputBlock().getNumChannels(),
|
||||
context.getOutputBlock().getNumChannels()));
|
||||
|
||||
for (size_t chan = 0; chan < numChannels; ++chan)
|
||||
processors[(int) chan]->process (MonoProcessContext<ProcessContext> (context, chan));
|
||||
}
|
||||
|
||||
typename StateType::Ptr state;
|
||||
|
||||
private:
|
||||
template <typename ProcessContext>
|
||||
struct MonoProcessContext : public ProcessContext
|
||||
{
|
||||
MonoProcessContext (const ProcessContext& multiChannelContext, size_t channelToUse)
|
||||
: ProcessContext (multiChannelContext), channel (channelToUse)
|
||||
{}
|
||||
|
||||
size_t channel;
|
||||
|
||||
typename ProcessContext::ConstAudioBlockType getInputBlock() const noexcept { return ProcessContext::getInputBlock() .getSingleChannelBlock (channel); }
|
||||
typename ProcessContext::AudioBlockType getOutputBlock() const noexcept { return ProcessContext::getOutputBlock().getSingleChannelBlock (channel); }
|
||||
};
|
||||
|
||||
juce::OwnedArray<MonoProcessorType> processors;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
81
deps/juce/modules/juce_dsp/processors/juce_ProcessorWrapper.h
vendored
Normal file
81
deps/juce/modules/juce_dsp/processors/juce_ProcessorWrapper.h
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Acts as a polymorphic base class for processors.
|
||||
This exposes the same set of methods that a processor must implement as virtual
|
||||
methods, so that you can use the ProcessorWrapper class to wrap an instance of
|
||||
a subclass, and then pass that around using ProcessorBase as a base class.
|
||||
@see ProcessorWrapper
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
struct ProcessorBase
|
||||
{
|
||||
ProcessorBase() = default;
|
||||
virtual ~ProcessorBase() = default;
|
||||
|
||||
virtual void prepare (const ProcessSpec&) = 0;
|
||||
virtual void process (const ProcessContextReplacing<float>&) = 0;
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wraps an instance of a given processor class, and exposes it through the
|
||||
ProcessorBase interface.
|
||||
@see ProcessorBase
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename ProcessorType>
|
||||
struct ProcessorWrapper : public ProcessorBase
|
||||
{
|
||||
void prepare (const ProcessSpec& spec) override
|
||||
{
|
||||
processor.prepare (spec);
|
||||
}
|
||||
|
||||
void process (const ProcessContextReplacing<float>& context) override
|
||||
{
|
||||
processor.process (context);
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
processor.reset();
|
||||
}
|
||||
|
||||
ProcessorType processor;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
260
deps/juce/modules/juce_dsp/processors/juce_StateVariableFilter.h
vendored
Normal file
260
deps/juce/modules/juce_dsp/processors/juce_StateVariableFilter.h
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Classes for state variable filter processing.
|
||||
*/
|
||||
namespace StateVariableFilter
|
||||
{
|
||||
template <typename NumericType>
|
||||
struct Parameters;
|
||||
|
||||
/**
|
||||
An IIR filter that can perform low, band and high-pass filtering on an audio
|
||||
signal, with 12 dB of attenuation per octave, using a TPT structure, designed
|
||||
for fast modulation (see Vadim Zavalishin's documentation about TPT
|
||||
structures for more information). Its behaviour is based on the analog
|
||||
state variable filter circuit.
|
||||
|
||||
Note: The bandpass here is not the one in the RBJ CookBook as its gain can be
|
||||
higher than 0 dB. For the classic 0 dB bandpass, we need to multiply the
|
||||
result by R2.
|
||||
|
||||
Note 2: Using this class prevents some loud audio artefacts commonly encountered when
|
||||
changing the cutoff frequency using other filter simulation structures and IIR
|
||||
filter classes. However, this class may still require additional smoothing for
|
||||
cutoff frequency changes.
|
||||
|
||||
see IIRFilter, SmoothedValue
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The NumericType is the underlying primitive type used by the SampleType (which
|
||||
could be either a primitive or vector)
|
||||
*/
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
/** A typedef for a ref-counted pointer to the coefficients object */
|
||||
using ParametersPtr = typename Parameters<NumericType>::Ptr;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
/** Creates a filter with default parameters. */
|
||||
[[deprecated ("The classes in the StateVariableFilter namespace are deprecated. you should "
|
||||
"use the equivalent functionality in the StateVariableTPTFilter class.")]]
|
||||
Filter() : parameters (new Parameters<NumericType>) { reset(); }
|
||||
|
||||
/** Creates a filter using some parameters. */
|
||||
[[deprecated ("The classes in the StateVariableFilter namespace are deprecated. you should "
|
||||
"use the equivalent functionality in the StateVariableTPTFilter class.")]]
|
||||
Filter (ParametersPtr parametersToUse) : parameters (std::move (parametersToUse)) { reset(); }
|
||||
#endif
|
||||
|
||||
/** Creates a copy of another filter. */
|
||||
Filter (const Filter&) = default;
|
||||
|
||||
/** Move constructor */
|
||||
Filter (Filter&&) = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Initialization of the filter */
|
||||
void prepare (const ProcessSpec&) noexcept { reset(); }
|
||||
|
||||
/** Resets the filter's processing pipeline. */
|
||||
void reset() noexcept { s1 = s2 = SampleType {0}; }
|
||||
|
||||
/** Ensure that the state variables are rounded to zero if the state
|
||||
variables are denormals. This is only needed if you are doing
|
||||
sample by sample processing.
|
||||
*/
|
||||
void snapToZero() noexcept { util::snapToZero (s1); util::snapToZero (s2); }
|
||||
|
||||
//==============================================================================
|
||||
/** The parameters of the state variable filter. It's up to the caller to ensure
|
||||
that these parameters are modified in a thread-safe way. */
|
||||
typename Parameters<NumericType>::Ptr parameters;
|
||||
|
||||
//==============================================================================
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
static_assert (std::is_same<typename ProcessContext::SampleType, SampleType>::value,
|
||||
"The sample-type of the filter must match the sample-type supplied to this process callback");
|
||||
|
||||
if (context.isBypassed)
|
||||
processInternal<true, ProcessContext> (context);
|
||||
else
|
||||
processInternal<false, ProcessContext> (context);
|
||||
}
|
||||
|
||||
/** Processes a single sample, without any locking or checking.
|
||||
Use this if you need processing of a single value. */
|
||||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType sample) noexcept
|
||||
{
|
||||
switch (parameters->type)
|
||||
{
|
||||
case Parameters<NumericType>::Type::lowPass: return processLoop<false, Parameters<NumericType>::Type::lowPass> (sample, *parameters); break;
|
||||
case Parameters<NumericType>::Type::bandPass: return processLoop<false, Parameters<NumericType>::Type::bandPass> (sample, *parameters); break;
|
||||
case Parameters<NumericType>::Type::highPass: return processLoop<false, Parameters<NumericType>::Type::highPass> (sample, *parameters); break;
|
||||
default: jassertfalse;
|
||||
}
|
||||
|
||||
return SampleType{0};
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <bool isBypassed, typename Parameters<NumericType>::Type type>
|
||||
SampleType JUCE_VECTOR_CALLTYPE processLoop (SampleType sample, Parameters<NumericType>& state) noexcept
|
||||
{
|
||||
y[2] = (sample - s1 * state.R2 - s1 * state.g - s2) * state.h;
|
||||
|
||||
y[1] = y[2] * state.g + s1;
|
||||
s1 = y[2] * state.g + y[1];
|
||||
|
||||
y[0] = y[1] * state.g + s2;
|
||||
s2 = y[1] * state.g + y[0];
|
||||
|
||||
return isBypassed ? sample : y[static_cast<size_t> (type)];
|
||||
}
|
||||
|
||||
template <bool isBypassed, typename Parameters<NumericType>::Type type>
|
||||
void processBlock (const SampleType* input, SampleType* output, size_t n) noexcept
|
||||
{
|
||||
auto state = *parameters;
|
||||
|
||||
for (size_t i = 0 ; i < n; ++i)
|
||||
output[i] = processLoop<isBypassed, type> (input[i], state);
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero();
|
||||
#endif
|
||||
|
||||
*parameters = state;
|
||||
}
|
||||
|
||||
template <bool isBypassed, typename ProcessContext>
|
||||
void processInternal (const ProcessContext& context) noexcept
|
||||
{
|
||||
auto&& inputBlock = context.getInputBlock();
|
||||
auto&& outputBlock = context.getOutputBlock();
|
||||
|
||||
// This class can only process mono signals. Use the ProcessorDuplicator class
|
||||
// to apply this filter on a multi-channel audio stream.
|
||||
jassert (inputBlock.getNumChannels() == 1);
|
||||
jassert (outputBlock.getNumChannels() == 1);
|
||||
|
||||
auto n = inputBlock.getNumSamples();
|
||||
auto* src = inputBlock .getChannelPointer (0);
|
||||
auto* dst = outputBlock.getChannelPointer (0);
|
||||
|
||||
switch (parameters->type)
|
||||
{
|
||||
case Parameters<NumericType>::Type::lowPass: processBlock<isBypassed, Parameters<NumericType>::Type::lowPass> (src, dst, n); break;
|
||||
case Parameters<NumericType>::Type::bandPass: processBlock<isBypassed, Parameters<NumericType>::Type::bandPass> (src, dst, n); break;
|
||||
case Parameters<NumericType>::Type::highPass: processBlock<isBypassed, Parameters<NumericType>::Type::highPass> (src, dst, n); break;
|
||||
default: jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::array<SampleType, 3> y;
|
||||
SampleType s1, s2;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (Filter)
|
||||
};
|
||||
|
||||
enum class StateVariableFilterType
|
||||
{
|
||||
lowPass,
|
||||
bandPass,
|
||||
highPass
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Structure used for the state variable filter parameters.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename NumericType>
|
||||
struct Parameters : public ProcessorState
|
||||
{
|
||||
//==============================================================================
|
||||
using Type = StateVariableFilterType;
|
||||
|
||||
//==============================================================================
|
||||
/** The type of the IIR filter */
|
||||
Type type = Type::lowPass;
|
||||
|
||||
/** Sets the cutoff frequency and resonance of the IIR filter.
|
||||
|
||||
Note: The bandwidth of the resonance increases with the value of the
|
||||
parameter. To have a standard 12 dB/octave filter, the value must be set
|
||||
at 1 / sqrt(2).
|
||||
*/
|
||||
void setCutOffFrequency (double sampleRate, NumericType frequency,
|
||||
NumericType resonance = static_cast<NumericType> (1.0 / MathConstants<double>::sqrt2)) noexcept
|
||||
{
|
||||
jassert (sampleRate > 0);
|
||||
jassert (resonance > NumericType (0));
|
||||
jassert (frequency > NumericType (0) && frequency <= NumericType (sampleRate * 0.5));
|
||||
|
||||
g = static_cast<NumericType> (std::tan (MathConstants<double>::pi * frequency / sampleRate));
|
||||
R2 = static_cast<NumericType> (1.0 / resonance);
|
||||
h = static_cast<NumericType> (1.0 / (1.0 + R2 * g + g * g));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** The Coefficients structure is ref-counted, so this is a handy type that can be used
|
||||
as a pointer to one.
|
||||
*/
|
||||
using Ptr = ReferenceCountedObjectPtr<Parameters>;
|
||||
|
||||
//==============================================================================
|
||||
Parameters() = default;
|
||||
Parameters (const Parameters& o) : g (o.g), R2 (o.R2), h (o.h) {}
|
||||
Parameters& operator= (const Parameters& o) noexcept { g = o.g; R2 = o.R2; h = o.h; return *this; }
|
||||
|
||||
//==============================================================================
|
||||
NumericType g = static_cast<NumericType> (std::tan (MathConstants<double>::pi * 200.0 / 44100.0));
|
||||
NumericType R2 = static_cast<NumericType> (MathConstants<double>::sqrt2);
|
||||
NumericType h = static_cast<NumericType> (1.0 / (1.0 + R2 * g + g * g));
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
137
deps/juce/modules/juce_dsp/processors/juce_StateVariableTPTFilter.cpp
vendored
Normal file
137
deps/juce/modules/juce_dsp/processors/juce_StateVariableTPTFilter.cpp
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
StateVariableTPTFilter<SampleType>::StateVariableTPTFilter()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::setType (Type newValue)
|
||||
{
|
||||
filterType = newValue;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::setCutoffFrequency (SampleType newCutoffFrequencyHz)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newCutoffFrequencyHz, static_cast<SampleType> (sampleRate * 0.5)));
|
||||
|
||||
cutoffFrequency = newCutoffFrequencyHz;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::setResonance (SampleType newResonance)
|
||||
{
|
||||
jassert (newResonance > static_cast<SampleType> (0));
|
||||
|
||||
resonance = newResonance;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
s1.resize (spec.numChannels);
|
||||
s2.resize (spec.numChannels);
|
||||
|
||||
reset();
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::reset()
|
||||
{
|
||||
reset (static_cast<SampleType> (0));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::reset (SampleType newValue)
|
||||
{
|
||||
for (auto v : { &s1, &s2 })
|
||||
std::fill (v->begin(), v->end(), newValue);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::snapToZero() noexcept
|
||||
{
|
||||
for (auto v : { &s1, &s2 })
|
||||
for (auto& element : *v)
|
||||
util::snapToZero (element);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
SampleType StateVariableTPTFilter<SampleType>::processSample (int channel, SampleType inputValue)
|
||||
{
|
||||
auto& ls1 = s1[(size_t) channel];
|
||||
auto& ls2 = s2[(size_t) channel];
|
||||
|
||||
auto yHP = h * (inputValue - ls1 * (g + R2) - ls2);
|
||||
|
||||
auto yBP = yHP * g + ls1;
|
||||
ls1 = yHP * g + yBP;
|
||||
|
||||
auto yLP = yBP * g + ls2;
|
||||
ls2 = yBP * g + yLP;
|
||||
|
||||
switch (filterType)
|
||||
{
|
||||
case Type::lowpass: return yLP;
|
||||
case Type::bandpass: return yBP;
|
||||
case Type::highpass: return yHP;
|
||||
default: return yLP;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void StateVariableTPTFilter<SampleType>::update()
|
||||
{
|
||||
g = static_cast<SampleType> (std::tan (juce::MathConstants<double>::pi * cutoffFrequency / sampleRate));
|
||||
R2 = static_cast<SampleType> (1.0 / resonance);
|
||||
h = static_cast<SampleType> (1.0 / (1.0 + R2 * g + g * g));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class StateVariableTPTFilter<float>;
|
||||
template class StateVariableTPTFilter<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
166
deps/juce/modules/juce_dsp/processors/juce_StateVariableTPTFilter.h
vendored
Normal file
166
deps/juce/modules/juce_dsp/processors/juce_StateVariableTPTFilter.h
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class StateVariableTPTFilterType
|
||||
{
|
||||
lowpass,
|
||||
bandpass,
|
||||
highpass
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** An IIR filter that can perform low, band and high-pass filtering on an audio
|
||||
signal, with 12 dB of attenuation per octave, using a TPT structure, designed
|
||||
for fast modulation (see Vadim Zavalishin's documentation about TPT
|
||||
structures for more information). Its behaviour is based on the analog
|
||||
state variable filter circuit.
|
||||
|
||||
Note: The bandpass here is not the one in the RBJ CookBook as its gain can be
|
||||
higher than 0 dB. For the classic 0 dB bandpass, we need to multiply the
|
||||
result by R2.
|
||||
|
||||
Note 2: Using this class prevents some loud audio artefacts commonly encountered when
|
||||
changing the cutoff frequency using other filter simulation structures and IIR
|
||||
filter classes. However, this class may still require additional smoothing for
|
||||
cutoff frequency changes.
|
||||
|
||||
see IIRFilter, SmoothedValue
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class StateVariableTPTFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using Type = StateVariableTPTFilterType;
|
||||
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
StateVariableTPTFilter();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the filter type. */
|
||||
void setType (Type newType);
|
||||
|
||||
/** Sets the cutoff frequency of the filter.
|
||||
|
||||
@param newFrequencyHz the new cutoff frequency in Hz.
|
||||
*/
|
||||
void setCutoffFrequency (SampleType newFrequencyHz);
|
||||
|
||||
/** Sets the resonance of the filter.
|
||||
|
||||
Note: The bandwidth of the resonance increases with the value of the
|
||||
parameter. To have a standard 12 dB / octave filter, the value must be set
|
||||
at 1 / sqrt(2).
|
||||
*/
|
||||
void setResonance (SampleType newResonance);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the type of the filter. */
|
||||
Type getType() const noexcept { return filterType; }
|
||||
|
||||
/** Returns the cutoff frequency of the filter. */
|
||||
SampleType getCutoffFrequency() const noexcept { return cutoffFrequency; }
|
||||
|
||||
/** Returns the resonance of the filter. */
|
||||
SampleType getResonance() const noexcept { return resonance; }
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the filter. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the filter. */
|
||||
void reset();
|
||||
|
||||
/** Resets the internal state variables of the filter to a given value. */
|
||||
void reset (SampleType newValue);
|
||||
|
||||
/** Ensure that the state variables are rounded to zero if the state
|
||||
variables are denormals. This is only needed if you are doing
|
||||
sample by sample processing.
|
||||
*/
|
||||
void snapToZero() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() <= s1.size());
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
|
||||
}
|
||||
|
||||
#if JUCE_DSP_ENABLE_SNAP_TO_ZERO
|
||||
snapToZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Processes one sample at a time on a given channel. */
|
||||
SampleType processSample (int channel, SampleType inputValue);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
SampleType g, h, R2;
|
||||
std::vector<SampleType> s1 { 2 }, s2 { 2 };
|
||||
|
||||
double sampleRate = 44100.0;
|
||||
Type filterType = Type::lowpass;
|
||||
SampleType cutoffFrequency = static_cast<SampleType> (1000.0),
|
||||
resonance = static_cast<SampleType> (1.0 / std::sqrt (2.0));
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
159
deps/juce/modules/juce_dsp/widgets/juce_Bias.h
vendored
Normal file
159
deps/juce/modules/juce_dsp/widgets/juce_Bias.h
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Adds a DC offset (voltage bias) to the audio samples.
|
||||
|
||||
This is a useful preprocessor for asymmetric waveshaping when a waveshaper is
|
||||
bookended by a bias on input and a DC-offset removing high pass filter on output.
|
||||
|
||||
This is an extremely simple bias implementation that simply adds a value to a signal.
|
||||
More complicated bias behaviours exist in real circuits - for your homework ;).
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class Bias
|
||||
{
|
||||
public:
|
||||
Bias() noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the DC bias
|
||||
@param newBias DC offset in range [-1, 1]
|
||||
*/
|
||||
void setBias (FloatType newBias) noexcept
|
||||
{
|
||||
jassert (newBias >= static_cast<FloatType> (-1) && newBias <= static_cast<FloatType> (1));
|
||||
bias.setTargetValue (newBias);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the DC bias
|
||||
@return DC bias, which should be in the range [-1, 1]
|
||||
*/
|
||||
FloatType getBias() const noexcept { return bias.getTargetValue(); }
|
||||
|
||||
/** Sets the length of the ramp used for smoothing gain changes. */
|
||||
void setRampDurationSeconds (double newDurationSeconds) noexcept
|
||||
{
|
||||
if (rampDurationSeconds != newDurationSeconds)
|
||||
{
|
||||
rampDurationSeconds = newDurationSeconds;
|
||||
updateRamp();
|
||||
}
|
||||
}
|
||||
|
||||
double getRampDurationSeconds() const noexcept { return rampDurationSeconds; }
|
||||
|
||||
//==============================================================================
|
||||
/** Called before processing starts */
|
||||
void prepare (const ProcessSpec& spec) noexcept
|
||||
{
|
||||
sampleRate = spec.sampleRate;
|
||||
updateRamp();
|
||||
}
|
||||
|
||||
void reset() noexcept
|
||||
{
|
||||
bias.reset (sampleRate, rampDurationSeconds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the result of processing a single sample. */
|
||||
template <typename SampleType>
|
||||
SampleType processSample (SampleType inputSample) noexcept
|
||||
{
|
||||
return inputSample + bias.getNextValue();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output buffers supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
auto&& inBlock = context.getInputBlock();
|
||||
auto&& outBlock = context.getOutputBlock();
|
||||
|
||||
jassert (inBlock.getNumChannels() == outBlock.getNumChannels());
|
||||
jassert (inBlock.getNumSamples() == outBlock.getNumSamples());
|
||||
|
||||
auto len = inBlock.getNumSamples();
|
||||
auto numChannels = inBlock.getNumChannels();
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
bias.skip (static_cast<int> (len));
|
||||
|
||||
if (context.usesSeparateInputAndOutputBlocks())
|
||||
outBlock.copyFrom (inBlock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (numChannels == 1)
|
||||
{
|
||||
auto* src = inBlock.getChannelPointer (0);
|
||||
auto* dst = outBlock.getChannelPointer (0);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = src[i] + bias.getNextValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386)
|
||||
auto* biases = static_cast<FloatType*> (alloca (sizeof (FloatType) * len));
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
biases[i] = bias.getNextValue();
|
||||
|
||||
for (size_t chan = 0; chan < numChannels; ++chan)
|
||||
FloatVectorOperations::add (outBlock.getChannelPointer (chan),
|
||||
inBlock.getChannelPointer (chan),
|
||||
biases, static_cast<int> (len));
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SmoothedValue<FloatType> bias;
|
||||
double sampleRate = 0, rampDurationSeconds = 0;
|
||||
|
||||
void updateRamp() noexcept
|
||||
{
|
||||
if (sampleRate > 0)
|
||||
bias.reset (sampleRate, rampDurationSeconds);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
141
deps/juce/modules/juce_dsp/widgets/juce_Chorus.cpp
vendored
Normal file
141
deps/juce/modules/juce_dsp/widgets/juce_Chorus.cpp
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
Chorus<SampleType>::Chorus()
|
||||
{
|
||||
auto oscFunction = [] (SampleType x) { return std::sin (x); };
|
||||
osc.initialise (oscFunction);
|
||||
|
||||
dryWet.setMixingRule (DryWetMixingRule::linear);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::setRate (SampleType newRateHz)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newRateHz, static_cast<SampleType> (100.0)));
|
||||
|
||||
rate = newRateHz;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::setDepth (SampleType newDepth)
|
||||
{
|
||||
jassert (isPositiveAndNotGreaterThan (newDepth, maxDepth));
|
||||
|
||||
depth = newDepth;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::setCentreDelay (SampleType newDelayMs)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newDelayMs, maxCentreDelayMs));
|
||||
|
||||
centreDelay = jlimit (static_cast<SampleType> (1.0), maxCentreDelayMs, newDelayMs);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::setFeedback (SampleType newFeedback)
|
||||
{
|
||||
jassert (newFeedback >= static_cast<SampleType> (-1.0) && newFeedback <= static_cast<SampleType> (1.0));
|
||||
|
||||
feedback = newFeedback;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::setMix (SampleType newMix)
|
||||
{
|
||||
jassert (isPositiveAndNotGreaterThan (newMix, static_cast<SampleType> (1.0)));
|
||||
|
||||
mix = newMix;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
const auto maxPossibleDelay = std::ceil ((maximumDelayModulation * maxDepth * oscVolumeMultiplier + maxCentreDelayMs)
|
||||
* sampleRate / 1000.0);
|
||||
delay = DelayLine<SampleType, DelayLineInterpolationTypes::Linear>{ static_cast<int> (maxPossibleDelay) };
|
||||
delay.prepare (spec);
|
||||
|
||||
dryWet.prepare (spec);
|
||||
feedbackVolume.resize (spec.numChannels);
|
||||
lastOutput.resize (spec.numChannels);
|
||||
|
||||
osc.prepare (spec);
|
||||
bufferDelayTimes.setSize (1, (int) spec.maximumBlockSize, false, false, true);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::reset()
|
||||
{
|
||||
std::fill (lastOutput.begin(), lastOutput.end(), static_cast<SampleType> (0));
|
||||
|
||||
delay.reset();
|
||||
osc.reset();
|
||||
dryWet.reset();
|
||||
|
||||
oscVolume.reset (sampleRate, 0.05);
|
||||
|
||||
for (auto& vol : feedbackVolume)
|
||||
vol.reset (sampleRate, 0.05);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Chorus<SampleType>::update()
|
||||
{
|
||||
osc.setFrequency (rate);
|
||||
oscVolume.setTargetValue (depth * oscVolumeMultiplier);
|
||||
dryWet.setWetMixProportion (mix);
|
||||
|
||||
for (auto& vol : feedbackVolume)
|
||||
vol.setTargetValue (feedback);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class Chorus<float>;
|
||||
template class Chorus<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
169
deps/juce/modules/juce_dsp/widgets/juce_Chorus.h
vendored
Normal file
169
deps/juce/modules/juce_dsp/widgets/juce_Chorus.h
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A simple chorus DSP widget that modulates the delay of a delay line in order to
|
||||
create sweeping notches in the magnitude frequency response.
|
||||
|
||||
This audio effect can be controlled via the speed and depth of the LFO controlling
|
||||
the frequency response, a mix control, a feedback control, and the centre delay
|
||||
of the modulation.
|
||||
|
||||
Note: To get classic chorus sounds try to use a centre delay time around 7-8 ms
|
||||
with a low feeback volume and a low depth. This effect can also be used as a
|
||||
flanger with a lower centre delay time and a lot of feedback, and as a vibrato
|
||||
effect if the mix value is 1.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Chorus
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
Chorus();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the rate (in Hz) of the LFO modulating the chorus delay line. This rate
|
||||
must be lower than 100 Hz.
|
||||
*/
|
||||
void setRate (SampleType newRateHz);
|
||||
|
||||
/** Sets the volume of the LFO modulating the chorus delay line (between 0 and 1).
|
||||
*/
|
||||
void setDepth (SampleType newDepth);
|
||||
|
||||
/** Sets the centre delay in milliseconds of the chorus delay line modulation.
|
||||
This delay must be between 1 and 100 ms.
|
||||
*/
|
||||
void setCentreDelay (SampleType newDelayMs);
|
||||
|
||||
/** Sets the feedback volume (between -1 and 1) of the chorus delay line.
|
||||
Negative values can be used to get specific chorus sounds.
|
||||
*/
|
||||
void setFeedback (SampleType newFeedback);
|
||||
|
||||
/** Sets the amount of dry and wet signal in the output of the chorus (between 0
|
||||
for full dry and 1 for full wet).
|
||||
*/
|
||||
void setMix (SampleType newMix);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumChannels() == lastOutput.size());
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
auto delayValuesBlock = AudioBlock<SampleType>(bufferDelayTimes).getSubBlock (0, numSamples);
|
||||
auto contextDelay = ProcessContextReplacing<SampleType> (delayValuesBlock);
|
||||
delayValuesBlock.clear();
|
||||
|
||||
osc.process (contextDelay);
|
||||
delayValuesBlock.multiplyBy (oscVolume);
|
||||
|
||||
auto* delaySamples = bufferDelayTimes.getWritePointer (0);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto lfo = jmax (static_cast<SampleType> (1.0), maximumDelayModulation * delaySamples[i] + centreDelay);
|
||||
delaySamples[i] = static_cast<SampleType> (lfo * sampleRate / 1000.0);
|
||||
}
|
||||
|
||||
dryWet.pushDrySamples (inputBlock);
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto input = inputSamples[i];
|
||||
auto output = input - lastOutput[channel];
|
||||
|
||||
delay.pushSample ((int) channel, output);
|
||||
delay.setDelay (delaySamples[i]);
|
||||
output = delay.popSample ((int) channel);
|
||||
|
||||
outputSamples[i] = output;
|
||||
lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
|
||||
}
|
||||
}
|
||||
|
||||
dryWet.mixWetSamples (outputBlock);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
Oscillator<SampleType> osc;
|
||||
DelayLine<SampleType, DelayLineInterpolationTypes::Linear> delay;
|
||||
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
|
||||
std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
|
||||
DryWetMixer<SampleType> dryWet;
|
||||
std::vector<SampleType> lastOutput { 2 };
|
||||
AudioBuffer<SampleType> bufferDelayTimes;
|
||||
|
||||
double sampleRate = 44100.0;
|
||||
SampleType rate = 1.0, depth = 0.25, feedback = 0.0, mix = 0.5,
|
||||
centreDelay = 7.0;
|
||||
|
||||
static constexpr SampleType maxDepth = 1.0,
|
||||
maxCentreDelayMs = 100.0,
|
||||
oscVolumeMultiplier = 0.5,
|
||||
maximumDelayModulation = 20.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
121
deps/juce/modules/juce_dsp/widgets/juce_Compressor.cpp
vendored
Normal file
121
deps/juce/modules/juce_dsp/widgets/juce_Compressor.cpp
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
Compressor<SampleType>::Compressor()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::setThreshold (SampleType newThreshold)
|
||||
{
|
||||
thresholddB = newThreshold;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::setRatio (SampleType newRatio)
|
||||
{
|
||||
jassert (newRatio >= static_cast<SampleType> (1.0));
|
||||
|
||||
ratio = newRatio;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::setAttack (SampleType newAttack)
|
||||
{
|
||||
attackTime = newAttack;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::setRelease (SampleType newRelease)
|
||||
{
|
||||
releaseTime = newRelease;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
envelopeFilter.prepare (spec);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::reset()
|
||||
{
|
||||
envelopeFilter.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
SampleType Compressor<SampleType>::processSample (int channel, SampleType inputValue)
|
||||
{
|
||||
// Ballistics filter with peak rectifier
|
||||
auto env = envelopeFilter.processSample (channel, inputValue);
|
||||
|
||||
// VCA
|
||||
auto gain = (env < threshold) ? static_cast<SampleType> (1.0)
|
||||
: std::pow (env * thresholdInverse, ratioInverse - static_cast<SampleType> (1.0));
|
||||
|
||||
// Output
|
||||
return gain * inputValue;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Compressor<SampleType>::update()
|
||||
{
|
||||
threshold = Decibels::decibelsToGain (thresholddB, static_cast<SampleType> (-200.0));
|
||||
thresholdInverse = static_cast<SampleType> (1.0) / threshold;
|
||||
ratioInverse = static_cast<SampleType> (1.0) / ratio;
|
||||
|
||||
envelopeFilter.setAttackTime (attackTime);
|
||||
envelopeFilter.setReleaseTime (releaseTime);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class Compressor<float>;
|
||||
template class Compressor<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
110
deps/juce/modules/juce_dsp/widgets/juce_Compressor.h
vendored
Normal file
110
deps/juce/modules/juce_dsp/widgets/juce_Compressor.h
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A simple compressor with standard threshold, ratio, attack time and release time
|
||||
controls.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Compressor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
Compressor();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the threshold in dB of the compressor.*/
|
||||
void setThreshold (SampleType newThreshold);
|
||||
|
||||
/** Sets the ratio of the compressor (must be higher or equal to 1).*/
|
||||
void setRatio (SampleType newRatio);
|
||||
|
||||
/** Sets the attack time in milliseconds of the compressor.*/
|
||||
void setAttack (SampleType newAttack);
|
||||
|
||||
/** Sets the release time in milliseconds of the compressor.*/
|
||||
void setRelease (SampleType newRelease);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs the processing operation on a single sample at a time. */
|
||||
SampleType processSample (int channel, SampleType inputValue);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
SampleType threshold, thresholdInverse, ratioInverse;
|
||||
BallisticsFilter<SampleType> envelopeFilter;
|
||||
|
||||
double sampleRate = 44100.0;
|
||||
SampleType thresholddB = 0.0, ratio = 1.0, attackTime = 1.0, releaseTime = 100.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
148
deps/juce/modules/juce_dsp/widgets/juce_Gain.h
vendored
Normal file
148
deps/juce/modules/juce_dsp/widgets/juce_Gain.h
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Applies a gain to audio samples as single samples or AudioBlocks.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class Gain
|
||||
{
|
||||
public:
|
||||
Gain() noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Applies a new gain as a linear value. */
|
||||
void setGainLinear (FloatType newGain) noexcept { gain.setTargetValue (newGain); }
|
||||
|
||||
/** Applies a new gain as a decibel value. */
|
||||
void setGainDecibels (FloatType newGainDecibels) noexcept { setGainLinear (Decibels::decibelsToGain<FloatType> (newGainDecibels)); }
|
||||
|
||||
/** Returns the current gain as a linear value. */
|
||||
FloatType getGainLinear() const noexcept { return gain.getTargetValue(); }
|
||||
|
||||
/** Returns the current gain in decibels. */
|
||||
FloatType getGainDecibels() const noexcept { return Decibels::gainToDecibels<FloatType> (getGainLinear()); }
|
||||
|
||||
/** Sets the length of the ramp used for smoothing gain changes. */
|
||||
void setRampDurationSeconds (double newDurationSeconds) noexcept
|
||||
{
|
||||
if (rampDurationSeconds != newDurationSeconds)
|
||||
{
|
||||
rampDurationSeconds = newDurationSeconds;
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the ramp duration in seconds. */
|
||||
double getRampDurationSeconds() const noexcept { return rampDurationSeconds; }
|
||||
|
||||
/** Returns true if the current value is currently being interpolated. */
|
||||
bool isSmoothing() const noexcept { return gain.isSmoothing(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Called before processing starts. */
|
||||
void prepare (const ProcessSpec& spec) noexcept
|
||||
{
|
||||
sampleRate = spec.sampleRate;
|
||||
reset();
|
||||
}
|
||||
|
||||
/** Resets the internal state of the gain */
|
||||
void reset() noexcept
|
||||
{
|
||||
if (sampleRate > 0)
|
||||
gain.reset (sampleRate, rampDurationSeconds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the result of processing a single sample. */
|
||||
template <typename SampleType>
|
||||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType s) noexcept
|
||||
{
|
||||
return s * gain.getNextValue();
|
||||
}
|
||||
|
||||
/** Processes the input and output buffers supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
auto&& inBlock = context.getInputBlock();
|
||||
auto&& outBlock = context.getOutputBlock();
|
||||
|
||||
jassert (inBlock.getNumChannels() == outBlock.getNumChannels());
|
||||
jassert (inBlock.getNumSamples() == outBlock.getNumSamples());
|
||||
|
||||
auto len = inBlock.getNumSamples();
|
||||
auto numChannels = inBlock.getNumChannels();
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
gain.skip (static_cast<int> (len));
|
||||
|
||||
if (context.usesSeparateInputAndOutputBlocks())
|
||||
outBlock.copyFrom (inBlock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (numChannels == 1)
|
||||
{
|
||||
auto* src = inBlock.getChannelPointer (0);
|
||||
auto* dst = outBlock.getChannelPointer (0);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = src[i] * gain.getNextValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386)
|
||||
auto* gains = static_cast<FloatType*> (alloca (sizeof (FloatType) * len));
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
gains[i] = gain.getNextValue();
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
for (size_t chan = 0; chan < numChannels; ++chan)
|
||||
FloatVectorOperations::multiply (outBlock.getChannelPointer (chan),
|
||||
inBlock.getChannelPointer (chan),
|
||||
gains, static_cast<int> (len));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SmoothedValue<FloatType> gain;
|
||||
double sampleRate = 0, rampDurationSeconds = 0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
176
deps/juce/modules/juce_dsp/widgets/juce_LadderFilter.cpp
vendored
Normal file
176
deps/juce/modules/juce_dsp/widgets/juce_LadderFilter.cpp
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
LadderFilter<SampleType>::LadderFilter() : state (2)
|
||||
{
|
||||
setSampleRate (SampleType (1000)); // intentionally setting unrealistic default
|
||||
// sample rate to catch missing initialisation bugs
|
||||
setResonance (SampleType (0));
|
||||
setDrive (SampleType (1.2));
|
||||
|
||||
mode = Mode::LPF24;
|
||||
setMode (Mode::LPF12);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::setMode (Mode newMode) noexcept
|
||||
{
|
||||
if (newMode == mode)
|
||||
return;
|
||||
|
||||
switch (newMode)
|
||||
{
|
||||
case Mode::LPF12: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0.5); break;
|
||||
case Mode::HPF12: A = {{ SampleType (1), SampleType (-2), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0); break;
|
||||
case Mode::BPF12: A = {{ SampleType (0), SampleType (0), SampleType (-1), SampleType (1), SampleType (0) }}; comp = SampleType (0.5); break;
|
||||
case Mode::LPF24: A = {{ SampleType (0), SampleType (0), SampleType (0), SampleType (0), SampleType (1) }}; comp = SampleType (0.5); break;
|
||||
case Mode::HPF24: A = {{ SampleType (1), SampleType (-4), SampleType (6), SampleType (-4), SampleType (1) }}; comp = SampleType (0); break;
|
||||
case Mode::BPF24: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (-2), SampleType (1) }}; comp = SampleType (0.5); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
static constexpr auto outputGain = SampleType (1.2);
|
||||
|
||||
for (auto& a : A)
|
||||
a *= outputGain;
|
||||
|
||||
mode = newMode;
|
||||
reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
setSampleRate (SampleType (spec.sampleRate));
|
||||
setNumChannels (spec.numChannels);
|
||||
reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::reset() noexcept
|
||||
{
|
||||
for (auto& s : state)
|
||||
s.fill (SampleType (0));
|
||||
|
||||
cutoffTransformSmoother.setCurrentAndTargetValue (cutoffTransformSmoother.getTargetValue());
|
||||
scaledResonanceSmoother.setCurrentAndTargetValue (scaledResonanceSmoother.getTargetValue());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::setCutoffFrequencyHz (SampleType newCutoff) noexcept
|
||||
{
|
||||
jassert (newCutoff > SampleType (0));
|
||||
cutoffFreqHz = newCutoff;
|
||||
updateCutoffFreq();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::setResonance (SampleType newResonance) noexcept
|
||||
{
|
||||
jassert (newResonance >= SampleType (0) && newResonance <= SampleType (1));
|
||||
resonance = newResonance;
|
||||
updateResonance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::setDrive (SampleType newDrive) noexcept
|
||||
{
|
||||
jassert (newDrive >= SampleType (1));
|
||||
|
||||
drive = newDrive;
|
||||
gain = std::pow (drive, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903);
|
||||
drive2 = drive * SampleType (0.04) + SampleType (0.96);
|
||||
gain2 = std::pow (drive2, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
SampleType LadderFilter<SampleType>::processSample (SampleType inputValue, size_t channelToUse) noexcept
|
||||
{
|
||||
auto& s = state[channelToUse];
|
||||
|
||||
const auto a1 = cutoffTransformValue;
|
||||
const auto g = a1 * SampleType (-1) + SampleType (1);
|
||||
const auto b0 = g * SampleType (0.76923076923);
|
||||
const auto b1 = g * SampleType (0.23076923076);
|
||||
|
||||
const auto dx = gain * saturationLUT (drive * inputValue);
|
||||
const auto a = dx + scaledResonanceValue * SampleType (-4) * (gain2 * saturationLUT (drive2 * s[4]) - dx * comp);
|
||||
|
||||
const auto b = b1 * s[0] + a1 * s[1] + b0 * a;
|
||||
const auto c = b1 * s[1] + a1 * s[2] + b0 * b;
|
||||
const auto d = b1 * s[2] + a1 * s[3] + b0 * c;
|
||||
const auto e = b1 * s[3] + a1 * s[4] + b0 * d;
|
||||
|
||||
s[0] = a;
|
||||
s[1] = b;
|
||||
s[2] = c;
|
||||
s[3] = d;
|
||||
s[4] = e;
|
||||
|
||||
return a * A[0] + b * A[1] + c * A[2] + d * A[3] + e * A[4];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::updateSmoothers() noexcept
|
||||
{
|
||||
cutoffTransformValue = cutoffTransformSmoother.getNextValue();
|
||||
scaledResonanceValue = scaledResonanceSmoother.getNextValue();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void LadderFilter<SampleType>::setSampleRate (SampleType newValue) noexcept
|
||||
{
|
||||
jassert (newValue > SampleType (0));
|
||||
cutoffFreqScaler = SampleType (-2.0 * juce::MathConstants<double>::pi) / newValue;
|
||||
|
||||
static constexpr SampleType smootherRampTimeSec = SampleType (0.05);
|
||||
cutoffTransformSmoother.reset (newValue, smootherRampTimeSec);
|
||||
scaledResonanceSmoother.reset (newValue, smootherRampTimeSec);
|
||||
|
||||
updateCutoffFreq();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class LadderFilter<float>;
|
||||
template class LadderFilter<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
153
deps/juce/modules/juce_dsp/widgets/juce_LadderFilter.h
vendored
Normal file
153
deps/juce/modules/juce_dsp/widgets/juce_LadderFilter.h
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
enum class LadderFilterMode
|
||||
{
|
||||
LPF12, // low-pass 12 dB/octave
|
||||
HPF12, // high-pass 12 dB/octave
|
||||
BPF12, // band-pass 12 dB/octave
|
||||
LPF24, // low-pass 24 dB/octave
|
||||
HPF24, // high-pass 24 dB/octave
|
||||
BPF24 // band-pass 24 dB/octave
|
||||
};
|
||||
|
||||
/**
|
||||
Multi-mode filter based on the Moog ladder filter.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class LadderFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using Mode = LadderFilterMode;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised filter. Call prepare() before first use. */
|
||||
LadderFilter();
|
||||
|
||||
/** Enables or disables the filter. If disabled it will simply pass through the input signal. */
|
||||
void setEnabled (bool isEnabled) noexcept { enabled = isEnabled; }
|
||||
|
||||
/** Sets filter mode. */
|
||||
void setMode (Mode newMode) noexcept;
|
||||
|
||||
/** Initialises the filter. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Returns the current number of channels. */
|
||||
size_t getNumChannels() const noexcept { return state.size(); }
|
||||
|
||||
/** Resets the internal state variables of the filter. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Sets the cutoff frequency of the filter.
|
||||
|
||||
@param newCutoff cutoff frequency in Hz
|
||||
*/
|
||||
void setCutoffFrequencyHz (SampleType newCutoff) noexcept;
|
||||
|
||||
/** Sets the resonance of the filter.
|
||||
|
||||
@param newResonance a value between 0 and 1; higher values increase the resonance and can result in self oscillation!
|
||||
*/
|
||||
void setResonance (SampleType newResonance) noexcept;
|
||||
|
||||
/** Sets the amount of saturation in the filter.
|
||||
|
||||
@param newDrive saturation amount; it can be any number greater than or equal to one. Higher values result in more distortion.
|
||||
*/
|
||||
void setDrive (SampleType newDrive) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() <= getNumChannels());
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (! enabled || context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < numSamples; ++n)
|
||||
{
|
||||
updateSmoothers();
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
outputBlock.getChannelPointer (ch)[n] = processSample (inputBlock.getChannelPointer (ch)[n], ch);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
SampleType processSample (SampleType inputValue, size_t channelToUse) noexcept;
|
||||
void updateSmoothers() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void setSampleRate (SampleType newValue) noexcept;
|
||||
void setNumChannels (size_t newValue) { state.resize (newValue); }
|
||||
void updateCutoffFreq() noexcept { cutoffTransformSmoother.setTargetValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); }
|
||||
void updateResonance() noexcept { scaledResonanceSmoother.setTargetValue (jmap (resonance, SampleType (0.1), SampleType (1.0))); }
|
||||
|
||||
//==============================================================================
|
||||
SampleType drive, drive2, gain, gain2, comp;
|
||||
|
||||
static constexpr size_t numStates = 5;
|
||||
std::vector<std::array<SampleType, numStates>> state;
|
||||
std::array<SampleType, numStates> A;
|
||||
|
||||
SmoothedValue<SampleType> cutoffTransformSmoother, scaledResonanceSmoother;
|
||||
SampleType cutoffTransformValue, scaledResonanceValue;
|
||||
|
||||
LookupTableTransform<SampleType> saturationLUT { [] (SampleType x) { return std::tanh (x); },
|
||||
SampleType (-5), SampleType (5), 128 };
|
||||
|
||||
SampleType cutoffFreqHz { SampleType (200) };
|
||||
SampleType resonance;
|
||||
|
||||
SampleType cutoffFreqScaler;
|
||||
|
||||
Mode mode;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
98
deps/juce/modules/juce_dsp/widgets/juce_Limiter.cpp
vendored
Normal file
98
deps/juce/modules/juce_dsp/widgets/juce_Limiter.cpp
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
void Limiter<SampleType>::setThreshold (SampleType newThreshold)
|
||||
{
|
||||
thresholddB = newThreshold;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Limiter<SampleType>::setRelease (SampleType newRelease)
|
||||
{
|
||||
releaseTime = newRelease;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Limiter<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
firstStageCompressor.prepare (spec);
|
||||
secondStageCompressor.prepare (spec);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Limiter<SampleType>::reset()
|
||||
{
|
||||
firstStageCompressor.reset();
|
||||
secondStageCompressor.reset();
|
||||
|
||||
outputVolume.reset (sampleRate, 0.001);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Limiter<SampleType>::update()
|
||||
{
|
||||
firstStageCompressor.setThreshold ((SampleType) -10.0);
|
||||
firstStageCompressor.setRatio ((SampleType) 4.0);
|
||||
firstStageCompressor.setAttack ((SampleType) 2.0);
|
||||
firstStageCompressor.setRelease ((SampleType) 200.0);
|
||||
|
||||
secondStageCompressor.setThreshold (thresholddB);
|
||||
secondStageCompressor.setRatio ((SampleType) 1000.0);
|
||||
secondStageCompressor.setAttack ((SampleType) 0.001);
|
||||
secondStageCompressor.setRelease (releaseTime);
|
||||
|
||||
auto ratioInverse = (SampleType) (1.0 / 4.0);
|
||||
|
||||
auto gain = (SampleType) std::pow (10.0, 10.0 * (1.0 - ratioInverse) / 40.0);
|
||||
gain *= Decibels::decibelsToGain (-thresholddB, (SampleType) -100.0);
|
||||
|
||||
outputVolume.setTargetValue (gain);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class Limiter<float>;
|
||||
template class Limiter<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
105
deps/juce/modules/juce_dsp/widgets/juce_Limiter.h
vendored
Normal file
105
deps/juce/modules/juce_dsp/widgets/juce_Limiter.h
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A simple limiter with standard threshold and release time controls, featuring
|
||||
two compressors and a hard clipper at 0 dB.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Limiter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
Limiter() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the threshold in dB of the limiter.*/
|
||||
void setThreshold (SampleType newThreshold);
|
||||
|
||||
/** Sets the release time in milliseconds of the limiter.*/
|
||||
void setRelease (SampleType newRelease);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
firstStageCompressor.process (context);
|
||||
|
||||
auto secondContext = ProcessContextReplacing<SampleType> (outputBlock);
|
||||
secondStageCompressor.process (secondContext);
|
||||
|
||||
outputBlock.multiplyBy (outputVolume);
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
FloatVectorOperations::clip (outputBlock.getChannelPointer (channel), outputBlock.getChannelPointer (channel),
|
||||
(SampleType) -1.0, (SampleType) 1.0, (int) numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
Compressor<SampleType> firstStageCompressor, secondStageCompressor;
|
||||
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> outputVolume;
|
||||
|
||||
double sampleRate = 44100.0;
|
||||
SampleType thresholddB = -10.0, releaseTime = 100.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
129
deps/juce/modules/juce_dsp/widgets/juce_NoiseGate.cpp
vendored
Normal file
129
deps/juce/modules/juce_dsp/widgets/juce_NoiseGate.cpp
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
NoiseGate<SampleType>::NoiseGate()
|
||||
{
|
||||
update();
|
||||
|
||||
RMSFilter.setLevelCalculationType (BallisticsFilterLevelCalculationType::RMS);
|
||||
RMSFilter.setAttackTime (static_cast<SampleType> (0.0));
|
||||
RMSFilter.setReleaseTime (static_cast<SampleType> (50.0));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::setThreshold (SampleType newValue)
|
||||
{
|
||||
thresholddB = newValue;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::setRatio (SampleType newRatio)
|
||||
{
|
||||
jassert (newRatio >= static_cast<SampleType> (1.0));
|
||||
|
||||
ratio = newRatio;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::setAttack (SampleType newAttack)
|
||||
{
|
||||
attackTime = newAttack;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::setRelease (SampleType newRelease)
|
||||
{
|
||||
releaseTime = newRelease;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
RMSFilter.prepare (spec);
|
||||
envelopeFilter.prepare (spec);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::reset()
|
||||
{
|
||||
RMSFilter.reset();
|
||||
envelopeFilter.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
SampleType NoiseGate<SampleType>::processSample (int channel, SampleType sample)
|
||||
{
|
||||
// RMS ballistics filter
|
||||
auto env = RMSFilter.processSample (channel, sample);
|
||||
|
||||
// Ballistics filter
|
||||
env = envelopeFilter.processSample (channel, env);
|
||||
|
||||
// VCA
|
||||
auto gain = (env > threshold) ? static_cast<SampleType> (1.0)
|
||||
: std::pow (env * thresholdInverse, currentRatio - static_cast<SampleType> (1.0));
|
||||
|
||||
// Output
|
||||
return gain * sample;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void NoiseGate<SampleType>::update()
|
||||
{
|
||||
threshold = Decibels::decibelsToGain (thresholddB, static_cast<SampleType> (-200.0));
|
||||
thresholdInverse = static_cast<SampleType> (1.0) / threshold;
|
||||
currentRatio = ratio;
|
||||
|
||||
envelopeFilter.setAttackTime (attackTime);
|
||||
envelopeFilter.setReleaseTime (releaseTime);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class NoiseGate<float>;
|
||||
template class NoiseGate<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
110
deps/juce/modules/juce_dsp/widgets/juce_NoiseGate.h
vendored
Normal file
110
deps/juce/modules/juce_dsp/widgets/juce_NoiseGate.h
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A simple noise gate with standard threshold, ratio, attack time and
|
||||
release time controls. Can be used as an expander if the ratio is low.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class NoiseGate
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
NoiseGate();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the threshold in dB of the noise-gate.*/
|
||||
void setThreshold (SampleType newThreshold);
|
||||
|
||||
/** Sets the ratio of the noise-gate (must be higher or equal to 1).*/
|
||||
void setRatio (SampleType newRatio);
|
||||
|
||||
/** Sets the attack time in milliseconds of the noise-gate.*/
|
||||
void setAttack (SampleType newAttack);
|
||||
|
||||
/** Sets the release time in milliseconds of the noise-gate.*/
|
||||
void setRelease (SampleType newRelease);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs the processing operation on a single sample at a time. */
|
||||
SampleType processSample (int channel, SampleType inputValue);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
SampleType threshold, thresholdInverse, currentRatio;
|
||||
BallisticsFilter<SampleType> envelopeFilter, RMSFilter;
|
||||
|
||||
double sampleRate = 44100.0;
|
||||
SampleType thresholddB = -100, ratio = 10.0, attackTime = 1.0, releaseTime = 100.0;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
251
deps/juce/modules/juce_dsp/widgets/juce_Oscillator.h
vendored
Normal file
251
deps/juce/modules/juce_dsp/widgets/juce_Oscillator.h
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Generates a signal based on a user-supplied function.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Oscillator
|
||||
{
|
||||
public:
|
||||
/** The NumericType is the underlying primitive type used by the SampleType (which
|
||||
could be either a primitive or vector)
|
||||
*/
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
/** Creates an uninitialised oscillator. Call initialise before first use. */
|
||||
Oscillator() = default;
|
||||
|
||||
/** Creates an oscillator with a periodic input function (-pi..pi).
|
||||
|
||||
If lookup table is not zero, then the function will be approximated
|
||||
with a lookup table.
|
||||
*/
|
||||
Oscillator (const std::function<NumericType (NumericType)>& function,
|
||||
size_t lookupTableNumPoints = 0)
|
||||
{
|
||||
initialise (function, lookupTableNumPoints);
|
||||
}
|
||||
|
||||
/** Returns true if the Oscillator has been initialised. */
|
||||
bool isInitialised() const noexcept { return static_cast<bool> (generator); }
|
||||
|
||||
/** Initialises the oscillator with a waveform. */
|
||||
void initialise (const std::function<NumericType (NumericType)>& function,
|
||||
size_t lookupTableNumPoints = 0)
|
||||
{
|
||||
if (lookupTableNumPoints != 0)
|
||||
{
|
||||
auto* table = new LookupTableTransform<NumericType> (function,
|
||||
-MathConstants<NumericType>::pi,
|
||||
MathConstants<NumericType>::pi,
|
||||
lookupTableNumPoints);
|
||||
|
||||
lookupTable.reset (table);
|
||||
generator = [table] (NumericType x) { return (*table) (x); };
|
||||
}
|
||||
else
|
||||
{
|
||||
generator = function;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the frequency of the oscillator. */
|
||||
void setFrequency (NumericType newFrequency, bool force = false) noexcept
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
frequency.setCurrentAndTargetValue (newFrequency);
|
||||
return;
|
||||
}
|
||||
|
||||
frequency.setTargetValue (newFrequency);
|
||||
}
|
||||
|
||||
/** Returns the current frequency of the oscillator. */
|
||||
NumericType getFrequency() const noexcept { return frequency.getTargetValue(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Called before processing starts. */
|
||||
void prepare (const ProcessSpec& spec) noexcept
|
||||
{
|
||||
sampleRate = static_cast<NumericType> (spec.sampleRate);
|
||||
rampBuffer.resize ((int) spec.maximumBlockSize);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
/** Resets the internal state of the oscillator */
|
||||
void reset() noexcept
|
||||
{
|
||||
phase.reset();
|
||||
|
||||
if (sampleRate > 0)
|
||||
frequency.reset (sampleRate, 0.05);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the result of processing a single sample. */
|
||||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType input) noexcept
|
||||
{
|
||||
jassert (isInitialised());
|
||||
auto increment = MathConstants<NumericType>::twoPi * frequency.getNextValue() / sampleRate;
|
||||
return input + generator (phase.advance (increment) - MathConstants<NumericType>::pi);
|
||||
}
|
||||
|
||||
/** Processes the input and output buffers supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
jassert (isInitialised());
|
||||
auto&& outBlock = context.getOutputBlock();
|
||||
auto&& inBlock = context.getInputBlock();
|
||||
|
||||
// this is an output-only processor
|
||||
jassert (outBlock.getNumSamples() <= static_cast<size_t> (rampBuffer.size()));
|
||||
|
||||
auto len = outBlock.getNumSamples();
|
||||
auto numChannels = outBlock.getNumChannels();
|
||||
auto inputChannels = inBlock.getNumChannels();
|
||||
auto baseIncrement = MathConstants<NumericType>::twoPi / sampleRate;
|
||||
|
||||
if (context.isBypassed)
|
||||
context.getOutputBlock().clear();
|
||||
|
||||
if (frequency.isSmoothing())
|
||||
{
|
||||
auto* buffer = rampBuffer.getRawDataPointer();
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
buffer[i] = phase.advance (baseIncrement * frequency.getNextValue())
|
||||
- MathConstants<NumericType>::pi;
|
||||
|
||||
if (! context.isBypassed)
|
||||
{
|
||||
size_t ch;
|
||||
|
||||
if (context.usesSeparateInputAndOutputBlocks())
|
||||
{
|
||||
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
|
||||
{
|
||||
auto* dst = outBlock.getChannelPointer (ch);
|
||||
auto* src = inBlock.getChannelPointer (ch);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = src[i] + generator (buffer[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
|
||||
{
|
||||
auto* dst = outBlock.getChannelPointer (ch);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] += generator (buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (; ch < numChannels; ++ch)
|
||||
{
|
||||
auto* dst = outBlock.getChannelPointer (ch);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = generator (buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto freq = baseIncrement * frequency.getNextValue();
|
||||
auto p = phase;
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
frequency.skip (static_cast<int> (len));
|
||||
p.advance (freq * static_cast<NumericType> (len));
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t ch;
|
||||
|
||||
if (context.usesSeparateInputAndOutputBlocks())
|
||||
{
|
||||
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
|
||||
{
|
||||
p = phase;
|
||||
auto* dst = outBlock.getChannelPointer (ch);
|
||||
auto* src = inBlock.getChannelPointer (ch);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = src[i] + generator (p.advance (freq) - MathConstants<NumericType>::pi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
|
||||
{
|
||||
p = phase;
|
||||
auto* dst = outBlock.getChannelPointer (ch);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] += generator (p.advance (freq) - MathConstants<NumericType>::pi);
|
||||
}
|
||||
}
|
||||
|
||||
for (; ch < numChannels; ++ch)
|
||||
{
|
||||
p = phase;
|
||||
auto* dst = outBlock.getChannelPointer (ch);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = generator (p.advance (freq) - MathConstants<NumericType>::pi);
|
||||
}
|
||||
}
|
||||
|
||||
phase = p;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::function<NumericType (NumericType)> generator;
|
||||
std::unique_ptr<LookupTableTransform<NumericType>> lookupTable;
|
||||
Array<NumericType> rampBuffer;
|
||||
SmoothedValue<NumericType> frequency { static_cast<NumericType> (440.0) };
|
||||
NumericType sampleRate = 48000.0;
|
||||
Phase<NumericType> phase;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
154
deps/juce/modules/juce_dsp/widgets/juce_Phaser.cpp
vendored
Normal file
154
deps/juce/modules/juce_dsp/widgets/juce_Phaser.cpp
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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>
|
||||
Phaser<SampleType>::Phaser()
|
||||
{
|
||||
auto oscFunction = [] (SampleType x) { return std::sin (x); };
|
||||
osc.initialise (oscFunction);
|
||||
|
||||
for (auto n = 0; n < numStages; ++n)
|
||||
{
|
||||
filters.add (new FirstOrderTPTFilter<SampleType>());
|
||||
filters[n]->setType (FirstOrderTPTFilterType::allpass);
|
||||
}
|
||||
|
||||
dryWet.setMixingRule (DryWetMixingRule::linear);
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::setRate (SampleType newRateHz)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newRateHz, static_cast<SampleType> (100.0)));
|
||||
|
||||
rate = newRateHz;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::setDepth (SampleType newDepth)
|
||||
{
|
||||
jassert (isPositiveAndNotGreaterThan (newDepth, static_cast<SampleType> (1.0)));
|
||||
|
||||
depth = newDepth;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::setCentreFrequency (SampleType newCentreHz)
|
||||
{
|
||||
jassert (isPositiveAndBelow (newCentreHz, static_cast<SampleType> (sampleRate * 0.5)));
|
||||
|
||||
centreFrequency = newCentreHz;
|
||||
normCentreFrequency = mapFromLog10 (centreFrequency, static_cast<SampleType> (20.0), static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::setFeedback (SampleType newFeedback)
|
||||
{
|
||||
jassert (newFeedback >= static_cast<SampleType> (-1.0) && newFeedback <= static_cast<SampleType> (1.0));
|
||||
|
||||
feedback = newFeedback;
|
||||
update();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::setMix (SampleType newMix)
|
||||
{
|
||||
jassert (isPositiveAndNotGreaterThan (newMix, static_cast<SampleType> (1.0)));
|
||||
|
||||
mix = newMix;
|
||||
update();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::prepare (const ProcessSpec& spec)
|
||||
{
|
||||
jassert (spec.sampleRate > 0);
|
||||
jassert (spec.numChannels > 0);
|
||||
|
||||
sampleRate = spec.sampleRate;
|
||||
|
||||
for (auto n = 0; n < numStages; ++n)
|
||||
filters[n]->prepare (spec);
|
||||
|
||||
dryWet.prepare (spec);
|
||||
feedbackVolume.resize (spec.numChannels);
|
||||
lastOutput.resize (spec.numChannels);
|
||||
|
||||
auto specDown = spec;
|
||||
specDown.sampleRate /= (double) maxUpdateCounter;
|
||||
specDown.maximumBlockSize = specDown.maximumBlockSize / (uint32) maxUpdateCounter + 1;
|
||||
|
||||
osc.prepare (specDown);
|
||||
bufferFrequency.setSize (1, (int) specDown.maximumBlockSize, false, false, true);
|
||||
|
||||
update();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::reset()
|
||||
{
|
||||
std::fill (lastOutput.begin(), lastOutput.end(), static_cast<SampleType> (0));
|
||||
|
||||
for (auto n = 0; n < numStages; ++n)
|
||||
filters[n]->reset();
|
||||
|
||||
osc.reset();
|
||||
dryWet.reset();
|
||||
|
||||
oscVolume.reset (sampleRate / (double) maxUpdateCounter, 0.05);
|
||||
|
||||
for (auto& vol : feedbackVolume)
|
||||
vol.reset (sampleRate, 0.05);
|
||||
|
||||
updateCounter = 0;
|
||||
}
|
||||
|
||||
template <typename SampleType>
|
||||
void Phaser<SampleType>::update()
|
||||
{
|
||||
osc.setFrequency (rate);
|
||||
oscVolume.setTargetValue (depth * (SampleType) 0.5);
|
||||
dryWet.setWetMixProportion (mix);
|
||||
|
||||
for (auto& vol : feedbackVolume)
|
||||
vol.setTargetValue (feedback);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template class Phaser<float>;
|
||||
template class Phaser<double>;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
206
deps/juce/modules/juce_dsp/widgets/juce_Phaser.h
vendored
Normal file
206
deps/juce/modules/juce_dsp/widgets/juce_Phaser.h
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
A 6 stage phaser that modulates first order all-pass filters to create sweeping
|
||||
notches in the magnitude frequency response.
|
||||
|
||||
This audio effect can be controlled with standard phaser parameters: the speed
|
||||
and depth of the LFO controlling the frequency response, a mix control, a
|
||||
feedback control, and the centre frequency of the modulation.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class Phaser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
Phaser();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the rate (in Hz) of the LFO modulating the phaser all-pass filters. This
|
||||
rate must be lower than 100 Hz.
|
||||
*/
|
||||
void setRate (SampleType newRateHz);
|
||||
|
||||
/** Sets the volume (between 0 and 1) of the LFO modulating the phaser all-pass
|
||||
filters.
|
||||
*/
|
||||
void setDepth (SampleType newDepth);
|
||||
|
||||
/** Sets the centre frequency (in Hz) of the phaser all-pass filters modulation.
|
||||
*/
|
||||
void setCentreFrequency (SampleType newCentreHz);
|
||||
|
||||
/** Sets the feedback volume (between -1 and 1) of the phaser. Negative can be
|
||||
used to get specific phaser sounds.
|
||||
*/
|
||||
void setFeedback (SampleType newFeedback);
|
||||
|
||||
/** Sets the amount of dry and wet signal in the output of the phaser (between 0
|
||||
for full dry and 1 for full wet).
|
||||
*/
|
||||
void setMix (SampleType newMix);
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the processor. */
|
||||
void prepare (const ProcessSpec& spec);
|
||||
|
||||
/** Resets the internal state variables of the processor. */
|
||||
void reset();
|
||||
|
||||
//==============================================================================
|
||||
/** Processes the input and output samples supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumChannels() == numChannels);
|
||||
jassert (inputBlock.getNumChannels() == lastOutput.size());
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
if (context.isBypassed)
|
||||
{
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
int numSamplesDown = 0;
|
||||
auto counter = updateCounter;
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
if (counter == 0)
|
||||
numSamplesDown++;
|
||||
|
||||
counter++;
|
||||
|
||||
if (counter == maxUpdateCounter)
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
if (numSamplesDown > 0)
|
||||
{
|
||||
auto freqBlock = AudioBlock<SampleType>(bufferFrequency).getSubBlock (0, (size_t) numSamplesDown);
|
||||
auto contextFreq = ProcessContextReplacing<SampleType> (freqBlock);
|
||||
freqBlock.clear();
|
||||
|
||||
osc.process (contextFreq);
|
||||
freqBlock.multiplyBy (oscVolume);
|
||||
}
|
||||
|
||||
auto* freqSamples = bufferFrequency.getWritePointer (0);
|
||||
|
||||
for (int i = 0; i < numSamplesDown; ++i)
|
||||
{
|
||||
auto lfo = jlimit (static_cast<SampleType> (0.0),
|
||||
static_cast<SampleType> (1.0),
|
||||
freqSamples[i] + normCentreFrequency);
|
||||
|
||||
freqSamples[i] = mapToLog10 (lfo, static_cast<SampleType> (20.0),
|
||||
static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
|
||||
}
|
||||
|
||||
auto currentFrequency = filters[0]->getCutoffFrequency();
|
||||
dryWet.pushDrySamples (inputBlock);
|
||||
|
||||
for (size_t channel = 0; channel < numChannels; ++channel)
|
||||
{
|
||||
counter = updateCounter;
|
||||
int k = 0;
|
||||
|
||||
auto* inputSamples = inputBlock .getChannelPointer (channel);
|
||||
auto* outputSamples = outputBlock.getChannelPointer (channel);
|
||||
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto input = inputSamples[i];
|
||||
auto output = input - lastOutput[channel];
|
||||
|
||||
if (i == 0 && counter != 0)
|
||||
for (int n = 0; n < numStages; ++n)
|
||||
filters[n]->setCutoffFrequency (currentFrequency);
|
||||
|
||||
if (counter == 0)
|
||||
{
|
||||
for (int n = 0; n < numStages; ++n)
|
||||
filters[n]->setCutoffFrequency (freqSamples[k]);
|
||||
|
||||
k++;
|
||||
}
|
||||
|
||||
for (int n = 0; n < numStages; ++n)
|
||||
output = filters[n]->processSample ((int) channel, output);
|
||||
|
||||
outputSamples[i] = output;
|
||||
lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
|
||||
|
||||
counter++;
|
||||
|
||||
if (counter == maxUpdateCounter)
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dryWet.mixWetSamples (outputBlock);
|
||||
updateCounter = (updateCounter + (int) numSamples) % maxUpdateCounter;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void update();
|
||||
|
||||
//==============================================================================
|
||||
Oscillator<SampleType> osc;
|
||||
OwnedArray<FirstOrderTPTFilter<SampleType>> filters;
|
||||
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
|
||||
std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
|
||||
DryWetMixer<SampleType> dryWet;
|
||||
std::vector<SampleType> lastOutput { 2 };
|
||||
AudioBuffer<SampleType> bufferFrequency;
|
||||
SampleType normCentreFrequency = 0.5;
|
||||
double sampleRate = 44100.0;
|
||||
|
||||
int updateCounter = 0;
|
||||
static constexpr int maxUpdateCounter = 4;
|
||||
|
||||
SampleType rate = 1.0, depth = 0.5, feedback = 0.0, mix = 0.5;
|
||||
SampleType centreFrequency = 1300.0;
|
||||
static constexpr int numStages = 6;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
115
deps/juce/modules/juce_dsp/widgets/juce_Reverb.h
vendored
Normal file
115
deps/juce/modules/juce_dsp/widgets/juce_Reverb.h
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Processor wrapper around juce::Reverb for easy integration into ProcessorChain.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
class Reverb
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised Reverb processor. Call prepare() before first use. */
|
||||
Reverb() = default;
|
||||
|
||||
//==============================================================================
|
||||
using Parameters = juce::Reverb::Parameters;
|
||||
|
||||
/** Returns the reverb's current parameters. */
|
||||
const Parameters& getParameters() const noexcept { return reverb.getParameters(); }
|
||||
|
||||
/** Applies a new set of parameters to the reverb.
|
||||
Note that this doesn't attempt to lock the reverb, so if you call this in parallel with
|
||||
the process method, you may get artifacts.
|
||||
*/
|
||||
void setParameters (const Parameters& newParams) { reverb.setParameters (newParams); }
|
||||
|
||||
/** Returns true if the reverb is enabled. */
|
||||
bool isEnabled() const noexcept { return enabled; }
|
||||
|
||||
/** Enables/disables the reverb. */
|
||||
void setEnabled (bool newValue) noexcept { enabled = newValue; }
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises the reverb. */
|
||||
void prepare (const ProcessSpec& spec)
|
||||
{
|
||||
reverb.setSampleRate (spec.sampleRate);
|
||||
}
|
||||
|
||||
/** Resets the reverb's internal state. */
|
||||
void reset() noexcept
|
||||
{
|
||||
reverb.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Applies the reverb to a mono or stereo buffer. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) noexcept
|
||||
{
|
||||
const auto& inputBlock = context.getInputBlock();
|
||||
auto& outputBlock = context.getOutputBlock();
|
||||
const auto numInChannels = inputBlock.getNumChannels();
|
||||
const auto numOutChannels = outputBlock.getNumChannels();
|
||||
const auto numSamples = outputBlock.getNumSamples();
|
||||
|
||||
jassert (inputBlock.getNumSamples() == numSamples);
|
||||
|
||||
outputBlock.copyFrom (inputBlock);
|
||||
|
||||
if (! enabled || context.isBypassed)
|
||||
return;
|
||||
|
||||
if (numInChannels == 1 && numOutChannels == 1)
|
||||
{
|
||||
reverb.processMono (outputBlock.getChannelPointer (0), (int) numSamples);
|
||||
}
|
||||
else if (numInChannels == 2 && numOutChannels == 2)
|
||||
{
|
||||
reverb.processStereo (outputBlock.getChannelPointer (0),
|
||||
outputBlock.getChannelPointer (1),
|
||||
(int) numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // invalid channel configuration
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
juce::Reverb reverb;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
85
deps/juce/modules/juce_dsp/widgets/juce_WaveShaper.h
vendored
Normal file
85
deps/juce/modules/juce_dsp/widgets/juce_WaveShaper.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Applies waveshaping to audio samples as single samples or AudioBlocks.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename FloatType, typename Function = FloatType (*) (FloatType)>
|
||||
struct WaveShaper
|
||||
{
|
||||
Function functionToUse;
|
||||
|
||||
//==============================================================================
|
||||
/** Called before processing starts. */
|
||||
void prepare (const ProcessSpec&) noexcept {}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the result of processing a single sample. */
|
||||
template <typename SampleType>
|
||||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType inputSample) const noexcept
|
||||
{
|
||||
return functionToUse (inputSample);
|
||||
}
|
||||
|
||||
/** Processes the input and output buffers supplied in the processing context. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) const noexcept
|
||||
{
|
||||
if (context.isBypassed)
|
||||
{
|
||||
if (context.usesSeparateInputAndOutputBlocks())
|
||||
context.getOutputBlock().copyFrom (context.getInputBlock());
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioBlock<FloatType>::process (context.getInputBlock(),
|
||||
context.getOutputBlock(),
|
||||
functionToUse);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() noexcept {}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Although clang supports C++17, their standard library still has no invoke_result
|
||||
// support. Remove the "|| JUCE_CLANG" once clang supports this properly!
|
||||
#if (! JUCE_CXX17_IS_AVAILABLE) || (JUCE_CLANG && ! JUCE_WINDOWS)
|
||||
template <typename Functor>
|
||||
static WaveShaper<typename std::result_of<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; }
|
||||
#else
|
||||
template <typename Functor>
|
||||
static WaveShaper<typename std::invoke_result<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; }
|
||||
#endif
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
Reference in New Issue
Block a user