@@ -19,9 +19,11 @@ | |||
#include "modules/juce_audio_utils/juce_audio_utils.h" | |||
#include "modules/juce_core/juce_core.h" | |||
#include "modules/juce_data_structures/juce_data_structures.h" | |||
#include "modules/juce_dsp/juce_dsp.h" | |||
#include "modules/juce_events/juce_events.h" | |||
#include "modules/juce_graphics/juce_graphics.h" | |||
#include "modules/juce_gui_basics/juce_gui_basics.h" | |||
#include "modules/juce_gui_extra/juce_gui_extra.h" | |||
#include "modules/juce_opengl/juce_opengl.h" | |||
#endif // JUCE_PLUGIN_MAIN_H_INCLUDED |
@@ -10,21 +10,25 @@ | |||
#define BUILD_JUCE_APPCONFIG_H_INCLUDED | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_utils 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_blocks_basics 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_box2d 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_core 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_cryptography 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_data_structures 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_dsp 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_events 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_graphics 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_opengl 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_opengl 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_osc 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_product_unlocking 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_video 0 | |||
// TODO new modules | |||
//============================================================================= | |||
@@ -39,7 +43,6 @@ | |||
//============================================================================= | |||
// juce_audio_devices | |||
//============================================================================= | |||
/** Config: JUCE_ASIO | |||
Enables ASIO audio devices (MS Windows only). | |||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed | |||
@@ -80,7 +83,6 @@ | |||
*/ | |||
#define JUCE_USE_ANDROID_OPENSLES 0 | |||
//============================================================================= | |||
/** Config: JUCE_USE_CDREADER | |||
Enables the AudioCDReader class (on supported platforms). | |||
*/ | |||
@@ -94,7 +96,6 @@ | |||
//============================================================================= | |||
// juce_audio_formats | |||
//============================================================================= | |||
/** Config: JUCE_USE_FLAC | |||
Enables the FLAC audio codec classes (available on all platforms). | |||
If your app doesn't need to read FLAC files, you might want to disable this to | |||
@@ -136,7 +137,6 @@ | |||
//============================================================================= | |||
// juce_audio_processors | |||
//============================================================================= | |||
/** Config: JUCE_PLUGINHOST_VST | |||
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be | |||
installed on your machine. | |||
@@ -180,7 +180,6 @@ | |||
//============================================================================= | |||
// juce_core | |||
//============================================================================= | |||
/** Config: JUCE_FORCE_DEBUG | |||
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, | |||
@@ -188,7 +187,6 @@ | |||
*/ | |||
#define JUCE_FORCE_DEBUG 0 | |||
//============================================================================= | |||
/** Config: JUCE_LOG_ASSERTIONS | |||
If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() | |||
@@ -202,7 +200,6 @@ | |||
*/ | |||
#define JUCE_LOG_ASSERTIONS 1 | |||
//============================================================================= | |||
/** Config: JUCE_CHECK_MEMORY_LEAKS | |||
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector | |||
@@ -214,7 +211,6 @@ | |||
#define JUCE_CHECK_MEMORY_LEAKS 0 | |||
#endif | |||
//============================================================================= | |||
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
In a Visual C++ build, this can be used to stop the required system libs being | |||
@@ -262,6 +258,73 @@ | |||
//============================================================================= | |||
// juce_data_structures | |||
//============================================================================= | |||
// juce_dsp | |||
/** 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. | |||
*/ | |||
#define JUCE_ASSERTION_FIRFILTER 0 | |||
/** 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. | |||
The folder containing the mkl_dfti.h header must be in your header | |||
search paths when using this flag. You also need to add all the necessary | |||
intel mkl libraries to the "External Libraries to Link" field in the | |||
Projucer. | |||
*/ | |||
#define JUCE_DSP_USE_INTEL_MKL 0 | |||
/** 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. | |||
*/ | |||
#define JUCE_DSP_USE_SHARED_FFTW 0 | |||
/** 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. | |||
*/ | |||
#define JUCE_DSP_USE_STATIC_FFTW 0 | |||
/** 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 exmaple, 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. | |||
*/ | |||
#define JUCE_DSP_ENABLE_SNAP_TO_ZERO 0 | |||
//============================================================================= | |||
// juce_events | |||
@@ -300,7 +363,6 @@ | |||
//============================================================================= | |||
// juce_gui_basics | |||
//============================================================================= | |||
/** Config: JUCE_ENABLE_REPAINT_DEBUGGING | |||
If this option is turned on, each area of the screen that gets repainted will | |||
flash in a random colour, so that you can see exactly which bits of your | |||
@@ -348,7 +410,6 @@ | |||
//============================================================================= | |||
// juce_gui_extra | |||
//============================================================================= | |||
/** Config: JUCE_WEB_BROWSER | |||
This lets you disable the WebBrowserComponent class (Mac and Windows). | |||
If you're not using any embedded web-pages, turning this off may reduce your code size. | |||
@@ -362,9 +423,11 @@ | |||
#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 0 | |||
//============================================================================= | |||
// drowaudio | |||
// juce_opengl | |||
//============================================================================= | |||
// drowaudio | |||
/** Config: DROWAUDIO_USE_FFTREAL | |||
Enables the FFTReal library. By default this is enabled except on the Mac | |||
where the Accelerate framework is preferred. However, if you do explicity | |||
@@ -391,7 +454,6 @@ | |||
//============================================================================= | |||
// juced | |||
//============================================================================= | |||
/** Config: JUCE_LASH | |||
Enables LASH support on Linux. | |||
Not enabled by default. | |||
@@ -439,9 +501,11 @@ | |||
#undef JUCE_MODULE_AVAILABLE_juce_graphics | |||
#undef JUCE_MODULE_AVAILABLE_juce_gui_basics | |||
#undef JUCE_MODULE_AVAILABLE_juce_gui_extra | |||
#undef JUCE_MODULE_AVAILABLE_juce_opengl | |||
#define JUCE_MODULE_AVAILABLE_juce_graphics 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_basics 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_extra 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_opengl 0 | |||
#undef JUCE_ALSA | |||
#undef JUCE_PLUGINHOST_LADSPA | |||
@@ -21,6 +21,7 @@ package.files = { | |||
"../source/modules/juce_core/juce_core.cpp", | |||
"../source/modules/juce_cryptography/juce_cryptography.cpp", | |||
"../source/modules/juce_data_structures/juce_data_structures.cpp", | |||
"../source/modules/juce_dsp/juce_dsp.cpp", | |||
"../source/modules/juce_events/juce_events.cpp" | |||
) | |||
} | |||
@@ -35,11 +36,12 @@ package.files = { | |||
"../source/modules/juce_core/juce_core.cpp", | |||
"../source/modules/juce_cryptography/juce_cryptography.cpp", | |||
"../source/modules/juce_data_structures/juce_data_structures.cpp", | |||
"../source/modules/juce_dsp/juce_dsp.cpp", | |||
"../source/modules/juce_events/juce_events.cpp", | |||
"../source/modules/juce_graphics/juce_graphics.cpp", | |||
"../source/modules/juce_gui_basics/juce_gui_basics.cpp", | |||
"../source/modules/juce_gui_extra/juce_gui_extra.cpp", | |||
"../source/modules/juce_tracktion_marketplace/juce_tracktion_marketplace.cpp" | |||
"../source/modules/juce_opengl/juce_opengl.cpp" | |||
) | |||
} | |||
end |
@@ -0,0 +1,549 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 Container> struct ElementType { using Type = typename Container::value_type; }; | |||
template <> struct ElementType<float> { using Type = float; }; | |||
template <> struct ElementType<double> { using Type = double; }; | |||
template <> struct ElementType<long double> { using Type = long double; }; | |||
} | |||
#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 AudioSampleBuffer 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. | |||
*/ | |||
template <typename SampleType> | |||
class AudioBlock | |||
{ | |||
public: | |||
//============================================================================== | |||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type; | |||
//============================================================================== | |||
/** Create a zero-sized AudioBlock. */ | |||
forcedinline AudioBlock() noexcept {} | |||
/** 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. | |||
*/ | |||
forcedinline 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. | |||
*/ | |||
forcedinline 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) 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 = sizeof (SampleType) - 1; | |||
heapBlockToUseForAllocation.malloc (channelListBytes + extraBytes + channelSize * numberOfChannels); | |||
auto* chanArray = reinterpret_cast<SampleType**> (heapBlockToUseForAllocation.getData()); | |||
channels = chanArray; | |||
auto* data = reinterpret_cast<SampleType*> (addBytesToPointer (chanArray, channelListBytes)); | |||
data = snapPointerToAlignment (data, sizeof (SampleType)); | |||
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. | |||
*/ | |||
AudioBlock (AudioBuffer<SampleType>& 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. | |||
*/ | |||
AudioBlock (AudioBuffer<SampleType>& buffer, size_t startSampleIndex) noexcept | |||
: channels (buffer.getArrayOfWritePointers()), | |||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())), | |||
startSample (startSampleIndex), | |||
numSamples (static_cast<size_t> (buffer.getNumSamples())) | |||
{ | |||
jassert (startSample < numSamples); | |||
} | |||
AudioBlock (const AudioBlock& other) noexcept = default; | |||
AudioBlock& operator= (const AudioBlock& other) noexcept = default; | |||
//============================================================================== | |||
forcedinline size_t getNumSamples() const noexcept { return numSamples; } | |||
forcedinline size_t getNumChannels() const noexcept { return static_cast<size_t> (numChannels); } | |||
/** Returns a raw pointer into one of the channels in this block. */ | |||
forcedinline const SampleType* getChannelPointer (size_t channel) const noexcept | |||
{ | |||
jassert (channel < numChannels); | |||
jassert (numSamples > 0); | |||
return *(channels + channel) + startSample; | |||
} | |||
/** Returns a raw pointer into one of the channels in this block. */ | |||
forcedinline SampleType* getChannelPointer (size_t channel) noexcept | |||
{ | |||
jassert (channel < numChannels); | |||
jassert (numSamples > 0); | |||
return *(channels + channel) + startSample; | |||
} | |||
/** Returns an AudioBlock that represents one of the channels in this block. */ | |||
forcedinline AudioBlock<SampleType> getSingleChannelBlock (size_t channel) const noexcept | |||
{ | |||
jassert (channel < numChannels); | |||
return AudioBlock (channels + channel, 1, startSample, numSamples); | |||
} | |||
/** Returns a subset of continguous channels | |||
@param channelStart First channel of the subset | |||
@param numChannelsToUse Count of channels in the subset | |||
*/ | |||
forcedinline AudioBlock<SampleType> getSubsetChannelBlock (size_t channelStart, size_t numChannelsToUse) noexcept | |||
{ | |||
jassert (channelStart < numChannels); | |||
jassert ((channelStart + numChannelsToUse) <= numChannels); | |||
return AudioBlock (channels + channelStart, numChannelsToUse, startSample, numSamples); | |||
} | |||
//============================================================================== | |||
/** Clear the memory described by this AudioBlock. */ | |||
forcedinline AudioBlock& clear() noexcept | |||
{ | |||
auto n = static_cast<int> (numSamples * sizeFactor); | |||
for (size_t ch = 0; ch < numChannels; ++ch) | |||
FloatVectorOperations::clear (channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Fill memory with value. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE fill (SampleType value) noexcept | |||
{ | |||
auto n = static_cast<int> (numSamples * sizeFactor); | |||
for (size_t ch = 0; ch < numChannels; ++ch) | |||
FloatVectorOperations::fill (channelPtr (ch), value, n); | |||
return *this; | |||
} | |||
/** Copy the values in src to the receiver. */ | |||
forcedinline AudioBlock& copy (const AudioBlock& src) noexcept | |||
{ | |||
auto maxChannels = jmin (src.numChannels, numChannels); | |||
auto n = static_cast<int> (jmin (src.numSamples, numSamples) * sizeFactor); | |||
for (size_t ch = 0; ch < maxChannels; ++ch) | |||
FloatVectorOperations::copy (channelPtr (ch), src.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Move memory within the receiver from the position srcPos to the position dstPos. | |||
If numElements is not specified then move will move the maximum amount of memory. | |||
*/ | |||
forcedinline AudioBlock& move (size_t srcPos, size_t dstPos, | |||
size_t numElements = std::numeric_limits<size_t>::max()) 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); | |||
return *this; | |||
} | |||
//============================================================================== | |||
/** Return a new AudioBlock pointing to a sub-block inside the receiver. 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 reciever which will | |||
will become the first element of the return value. | |||
@param newLength The number of elements of the newly created sub-block. | |||
*/ | |||
inline 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 the receiver. 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 reciever which will | |||
will become the first element of the return value. | |||
The return value will include all subsequent elements | |||
of the receiver. | |||
*/ | |||
inline AudioBlock getSubBlock (size_t newOffset) const noexcept | |||
{ | |||
return getSubBlock (newOffset, getNumSamples() - newOffset); | |||
} | |||
//============================================================================== | |||
/** Adds a fixed value to the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE add (SampleType value) noexcept | |||
{ | |||
auto n = static_cast<int> (numSamples * sizeFactor); | |||
for (size_t ch = 0; ch < numChannels; ++ch) | |||
FloatVectorOperations::add (channelPtr (ch), value, n); | |||
return *this; | |||
} | |||
/** Adds the source values to the receiver. */ | |||
forcedinline AudioBlock& add (const AudioBlock& src) 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 (channelPtr (ch), src.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Adds a fixed value to each source value and stores it in the destination array of the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE add (const AudioBlock& src, SampleType value) 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 (channelPtr (ch), src.channelPtr (ch), value, n); | |||
return *this; | |||
} | |||
/** Adds each source1 value to the corresponding source2 value and stores it in the destination array of the receiver. */ | |||
forcedinline AudioBlock& add (const AudioBlock& src1, const AudioBlock& src2) 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 (channelPtr (ch), src1.channelPtr (ch), src2.getChannelPointer (ch), n); | |||
return *this; | |||
} | |||
/** Subtracts a fixed value from the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE subtract (SampleType value) noexcept | |||
{ | |||
return add (value * static_cast<SampleType> (-1.0)); | |||
} | |||
/** Subtracts the source values from the receiver. */ | |||
forcedinline AudioBlock& subtract (const AudioBlock& src) 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 (channelPtr (ch), src.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Subtracts a fixed value from each source value and stores it in the destination array of the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE subtract (const AudioBlock& src, SampleType value) noexcept | |||
{ | |||
return add (src, static_cast<SampleType> (-1.0) * value); | |||
} | |||
/** Subtracts each source2 value from the corresponding source1 value and stores it in the destination array of the receiver. */ | |||
forcedinline AudioBlock& subtract (const AudioBlock& src1, const AudioBlock& src2) 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 (channelPtr (ch), src1.channelPtr (ch), src2.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Multiplies a fixed value to the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE multiply (SampleType value) noexcept | |||
{ | |||
auto n = static_cast<int> (numSamples * sizeFactor); | |||
for (size_t ch = 0; ch < numChannels; ++ch) | |||
FloatVectorOperations::multiply (channelPtr (ch), value, n); | |||
return *this; | |||
} | |||
/** Multiplies the source values to the receiver. */ | |||
forcedinline AudioBlock& multiply (const AudioBlock& src) 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 (channelPtr (ch), src.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Multiplies a fixed value to each source value and stores it in the destination array of the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE multiply (const AudioBlock& src, SampleType value) 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 (channelPtr (ch), src.channelPtr (ch), value, n); | |||
return *this; | |||
} | |||
/** Multiplies each source1 value to the corresponding source2 value and stores it in the destination array of the receiver. */ | |||
forcedinline AudioBlock& multiply (const AudioBlock& src1, const AudioBlock& src2) 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 (channelPtr (ch), src1.channelPtr (ch), src2.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Multiplies each value in src with factor and adds the result to the receiver. */ | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE addWithMultiply (const AudioBlock& src, SampleType factor) 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 (channelPtr (ch), src.channelPtr (ch), factor, n); | |||
return *this; | |||
} | |||
/** Multiplies each value in srcA with the corresponding value in srcB and adds the result to the receiver. */ | |||
forcedinline AudioBlock& addWithMultiply (const AudioBlock& src1, const AudioBlock& src2) 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 (channelPtr (ch), src1.channelPtr (ch), src2.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Negates each value of the receiver. */ | |||
forcedinline AudioBlock& negate() noexcept | |||
{ | |||
return multiply (static_cast<SampleType> (-1.0)); | |||
} | |||
/** Negates each value of source and stores it in the receiver. */ | |||
forcedinline AudioBlock& replaceWithNegativeOf (const AudioBlock& src) 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 (channelPtr (ch), src.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Takes the absolute value of each element of src and stores it inside the receiver. */ | |||
forcedinline AudioBlock& replaceWithAbsoluteValueOf (const AudioBlock& src) 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 (channelPtr (ch), src.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Each element of receiver will be the minimum of the corresponding element of the source arrays. */ | |||
forcedinline AudioBlock& min (const AudioBlock& src1, const AudioBlock& src2) 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 (channelPtr (ch), src1.channelPtr (ch), src2.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Each element of the receiver will be the maximum of the corresponding element of the source arrays. */ | |||
forcedinline AudioBlock& max (AudioBlock src1, AudioBlock src2) 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 (channelPtr (ch), src1.channelPtr (ch), src2.channelPtr (ch), n); | |||
return *this; | |||
} | |||
/** Finds the minimum and maximum value of the buffer. */ | |||
forcedinline Range<NumericType> findMinAndMax() const noexcept | |||
{ | |||
Range<NumericType> minmax; | |||
auto n = static_cast<int> (numSamples * sizeFactor); | |||
for (size_t ch = 0; ch < numChannels; ++ch) | |||
minmax = minmax.getUnionWith (FloatVectorOperations::findMinAndMax (channelPtr (ch), n)); | |||
return minmax; | |||
} | |||
//============================================================================== | |||
// convenient operator wrappers | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE operator+= (SampleType src) noexcept { return add (src); } | |||
forcedinline AudioBlock& operator+= (AudioBlock src) noexcept { return add (src); } | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE operator-= (SampleType src) noexcept { return subtract (src); } | |||
forcedinline AudioBlock& operator-= (AudioBlock src) noexcept { return subtract (src); } | |||
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE operator*= (SampleType src) noexcept { return multiply (src); } | |||
forcedinline AudioBlock& operator*= (AudioBlock src) noexcept { return multiply (src); } | |||
//============================================================================== | |||
// This class can only be used with floating point types | |||
static_assert (std::is_same<SampleType, float>::value || std::is_same<SampleType, double>::value | |||
#if JUCE_USE_SIMD | |||
|| std::is_same<SampleType, SIMDRegister<float> >::value || std::is_same<SampleType, SIMDRegister<double> >::value | |||
#endif | |||
, "AudioBlock only supports single or double precision floating point types"); | |||
//============================================================================== | |||
template <typename FunctionType> | |||
static void process (const AudioBlock<SampleType>& inBlock, | |||
AudioBlock<SampleType>& outBlock, | |||
const FunctionType& function) | |||
{ | |||
auto len = inBlock.getNumSamples(); | |||
auto numChans = inBlock.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* channelPtr (size_t ch) noexcept { return reinterpret_cast<NumericType*> (getChannelPointer (ch)); } | |||
const NumericType* channelPtr (size_t ch) const noexcept { return reinterpret_cast<const NumericType*> (getChannelPointer (ch)); } | |||
//============================================================================== | |||
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; | |||
SampleType* const* channels; | |||
ChannelCountType numChannels = 0; | |||
size_t startSample = 0, numSamples = 0; | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,414 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 availabe on SIMD machines. Use JUCE_USE_SIMD to query | |||
if SIMD is avaialble 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 suffeciently 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 suffeciently aligned for SIMD vector operations. | |||
Note that using SIMDRegister without enabling optimizations will result | |||
in code with very poor performance. | |||
*/ | |||
template <typename Type> | |||
struct SIMDRegister | |||
{ | |||
//============================================================================== | |||
/** The type that represents the individual constituents of the SIMD Register */ | |||
typedef Type ElementType; | |||
/** STL compatible value_type definition (same as ElementType). */ | |||
typedef ElementType value_type; | |||
/** The corresponding primitive integer type, for example, this will be int32_t | |||
if type is a float. */ | |||
typedef typename SIMDInternal::MaskTypeFor<ElementType>::type MaskType; | |||
//============================================================================== | |||
// Here are some types which are needed internally | |||
/** The native primitive type (used internally). */ | |||
typedef typename SIMDInternal::PrimitiveType<ElementType>::type PrimitiveType; | |||
/** The native operations for this platform and type combination (used internally) */ | |||
typedef SIMDNativeOps<PrimitiveType> NativeOps; | |||
/** The native type (used internally). */ | |||
typedef typename NativeOps::vSIMDType vSIMDType; | |||
/** The corresponding integer SIMDRegister type (used internally). */ | |||
typedef SIMDRegister<MaskType> vMaskType; | |||
/** The internal native type for the corresponding mask type (used internally). */ | |||
typedef typename vMaskType::vSIMDType vMaskSIMDType; | |||
/** Wrapper for operations which need to be handled differently for complex | |||
and scalar types (used internally). */ | |||
typedef CmplxSIMDOps<ElementType> CmplxOps; | |||
//============================================================================== | |||
/** 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; | |||
//============================================================================== | |||
/** 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. */ | |||
inline 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). */ | |||
inline static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {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 operator[] (size_t idx) const noexcept | |||
{ | |||
jassert (idx < SIMDNumElements); | |||
return reinterpret_cast<const ElementType*> (&value) [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 ElementType& JUCE_VECTOR_CALLTYPE operator[] (size_t idx) noexcept | |||
{ | |||
jassert (idx < SIMDNumElements); | |||
return reinterpret_cast<ElementType*> (&value) [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; } | |||
/** Subtracts 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 reciver 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 reciver 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 reciver 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 reciver 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 reciver 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 reciver 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 difference 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 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 inline 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 inline 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 inline 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 inline 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 inline 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 inline 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 inline 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 inline SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; } | |||
//============================================================================== | |||
/** Multiplies a and b and adds the result to c. */ | |||
static inline 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); } | |||
//============================================================================== | |||
/** Checks if the given pointer is suffeciently aligned for using SIMD operations. */ | |||
static inline bool isSIMDAligned (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 inline ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept | |||
{ | |||
return snapPointerToAlignment (ptr, SIMDRegisterSize); | |||
} | |||
private: | |||
static inline vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept | |||
{ | |||
union | |||
{ | |||
vSIMDType in; | |||
vMaskSIMDType out; | |||
} u; | |||
u.in = a; | |||
return vMaskType::fromNative (u.out); | |||
} | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept | |||
{ | |||
union | |||
{ | |||
vMaskSIMDType in; | |||
vSIMDType out; | |||
} u; | |||
u.in = a; | |||
return u.out; | |||
} | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept | |||
{ | |||
union | |||
{ | |||
vMaskSIMDType in; | |||
vSIMDType out; | |||
} u; | |||
u.in = CmplxSIMDOps<MaskType>::expand (a); | |||
return u.out; | |||
} | |||
}; | |||
//============================================================================== | |||
/* 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 | |||
{ | |||
typedef typename SIMDNativeOps<Scalar>::vSIMDType vSIMDType; | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept | |||
{ | |||
return SIMDNativeOps<Scalar>::expand (s); | |||
} | |||
static inline Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept | |||
{ | |||
return SIMDNativeOps<Scalar>::sum (a); | |||
} | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept | |||
{ | |||
return SIMDNativeOps<Scalar>::mul (a, b); | |||
} | |||
static inline 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> > | |||
{ | |||
typedef typename SIMDNativeOps<Scalar>::vSIMDType vSIMDType; | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex<Scalar> s) noexcept | |||
{ | |||
const int n = sizeof (vSIMDType) / sizeof (Scalar); | |||
union | |||
{ | |||
vSIMDType v; | |||
Scalar floats[n]; | |||
} u; | |||
for (int i = 0; i < n; ++i) | |||
u.floats[i] = (i & 1) == 0 ? s.real() : s.imag(); | |||
return u.v; | |||
} | |||
static inline std::complex<Scalar> JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept | |||
{ | |||
vSIMDType result = SIMDNativeOps<Scalar>::oddevensum (a); | |||
const Scalar* ptr = reinterpret_cast<const Scalar*> (&result); | |||
return std::complex<Scalar> (ptr[0], ptr[1]); | |||
} | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept | |||
{ | |||
return SIMDNativeOps<Scalar>::cmplxmul (a, b); | |||
} | |||
static inline vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept | |||
{ | |||
return SIMDNativeOps<Scalar>::add (a, SIMDNativeOps<Scalar>::cmplxmul (b, c)); | |||
} | |||
}; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
namespace util | |||
{ | |||
template <typename Type> | |||
inline void snapToZero (SIMDRegister<Type>&) noexcept {} | |||
} | |||
#endif | |||
} // 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 |
@@ -0,0 +1,676 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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> | |||
static void fillRandom (type* dst, const int size, Random& random) | |||
{ | |||
bool is_signed = std::is_signed<type>::value; | |||
for (int i = 0; i < size; ++i) | |||
{ | |||
if (is_signed) | |||
{ | |||
*dst++ = static_cast<type> ((random.nextFloat() * 16.0) - 8.0); | |||
} | |||
else | |||
{ | |||
*dst++ = static_cast<type> (random.nextFloat() * 8.0); | |||
} | |||
} | |||
} | |||
template <typename type> | |||
static void fillRandom (std::complex<type>* dst, const int size, Random& random) | |||
{ | |||
for (int i = 0; i < size; ++i) | |||
{ | |||
type real, imag; | |||
real = static_cast<type> ((random.nextFloat() * 16.0) - 8.0); | |||
imag = static_cast<type> ((random.nextFloat() * 16.0) - 8.0); | |||
*dst++ = std::complex<type> (real, imag); | |||
} | |||
} | |||
// Avoid visual studio warning | |||
template <typename type> | |||
static type safeAbs (type a) | |||
{ | |||
return static_cast<type> (fabs ((double) a)); | |||
} | |||
template <typename type> | |||
static type safeAbs (std::complex<type> a) | |||
{ | |||
return 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 dependant. | |||
class SIMDRegisterUnitTests : public UnitTest | |||
{ | |||
public: | |||
SIMDRegisterUnitTests() : UnitTest ("SIMDRegister UnitTests") {} | |||
//============================================================================== | |||
// Some helper classes | |||
template <typename type> | |||
static bool allValuesEqualTo (const SIMDRegister<type>& vec, const type scalar) | |||
{ | |||
// as we do not want to rely on the access operator we cast this to a primitive pointer | |||
const type* ptr = reinterpret_cast<const type*> (&vec); | |||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i) | |||
if (ptr[i] != scalar) return false; | |||
return true; | |||
} | |||
template <typename type> | |||
static bool vecEqualToArray (const SIMDRegister<type>& vec, const type* array) | |||
{ | |||
const type* ptr = reinterpret_cast<const type*> (&vec); | |||
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) | |||
{ | |||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i) | |||
vec[i] = ptr[i]; | |||
} | |||
//============================================================================== | |||
// Someuseful 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)); | |||
{ | |||
SIMDRegister<type> a; | |||
type* ptr = reinterpret_cast<type*>(&a); | |||
SIMDRegister_test_internal::fillRandom (ptr, SIMDRegister<type>::SIMDNumElements, random); | |||
u.expect (vecEqualToArray (SIMDRegister<type> (a), ptr)); | |||
} | |||
} | |||
}; | |||
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::fillRandom (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, b, c; | |||
type array_a [SIMDRegister<type>::SIMDNumElements]; | |||
type array_b [SIMDRegister<type>::SIMDNumElements]; | |||
type array_c [SIMDRegister<type>::SIMDNumElements]; | |||
SIMDRegister_test_internal::fillRandom (array_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (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::fillRandom (array_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (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::fillRandom (array_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (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 | |||
{ | |||
SIMDRegister<type> floatVersion; | |||
vMaskType intVersion; | |||
} a, b; | |||
vMaskType bitmask = vMaskType::expand (static_cast<MaskType> (1) << (sizeof (MaskType) - 1)); | |||
SIMDRegister_test_internal::fillRandom (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); | |||
u.expect (vecEqualToArray (a.floatVersion, reinterpret_cast<const type*> (&b.floatVersion))); | |||
} | |||
// 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* conv_a = reinterpret_cast<type*> (array_a); | |||
type* conv_c = reinterpret_cast<type*> (array_c); | |||
SIMDRegister_test_internal::fillRandom (conv_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<MaskType>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (conv_c, SIMDRegister<type>::SIMDNumElements, random); | |||
copy (a, conv_a); copy (b, array_b); copy (c, conv_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]); | |||
Operation::template inplace<SIMDRegister<type>, vMaskType> (a, b); | |||
u.expect (vecEqualToArray (a, conv_a)); | |||
u.expect (vecEqualToArray (b, array_b)); | |||
SIMDRegister_test_internal::fillRandom (conv_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<MaskType>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (conv_c, SIMDRegister<type>::SIMDNumElements, random); | |||
copy (a, conv_a); copy (b, array_b); copy (c, conv_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)); | |||
Operation::template inplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9)); | |||
u.expect (vecEqualToArray (a, conv_a)); | |||
u.expect (vecEqualToArray (b, array_b)); | |||
// set-up again | |||
SIMDRegister_test_internal::fillRandom (conv_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<MaskType>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (conv_c, SIMDRegister<type>::SIMDNumElements, random); | |||
copy (a, conv_a); copy (b, array_b); copy (c, conv_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]); | |||
} | |||
c = Operation::template outofplace<SIMDRegister<type>, vMaskType> (a, b); | |||
u.expect (vecEqualToArray (a, conv_a)); | |||
u.expect (vecEqualToArray (b, array_b)); | |||
u.expect (vecEqualToArray (c, conv_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)); | |||
c = Operation::template outofplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9)); | |||
u.expect (vecEqualToArray (a, conv_a)); | |||
u.expect (vecEqualToArray (b, array_b)); | |||
u.expect (vecEqualToArray (c, conv_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::fillRandom (array_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (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, b; | |||
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 )); | |||
} | |||
} | |||
}; | |||
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::fillRandom (array_a, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_b, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (array_c, SIMDRegister<type>::SIMDNumElements, random); | |||
SIMDRegister_test_internal::fillRandom (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, b, vMin, vMax; | |||
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::fillRandom (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); | |||
} | |||
}; | |||
//============================================================================== | |||
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); | |||
} | |||
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<CheckMinMax> ("CheckMinMax"); | |||
runTestForAllTypes<CheckMultiplyAdd> ("CheckMultiplyAdd"); | |||
runTestForAllTypes<CheckSum> ("CheckSum"); | |||
} | |||
}; | |||
static SIMDRegisterUnitTests SIMDRegisterUnitTests; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,625 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 normalizedFrequency = frequency / sampleRate; | |||
for (size_t i = 0; i <= order; ++i) | |||
{ | |||
if (i == order * 0.5) | |||
{ | |||
c[i] = static_cast<FloatType> (normalizedFrequency * 2); | |||
} | |||
else | |||
{ | |||
auto indice = double_Pi * (static_cast<double> (i) - 0.5 * static_cast<double> (order)); | |||
c[i] = static_cast<FloatType> (std::sin (2.0 * indice * normalizedFrequency) / 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 normalizedTransitionWidth, | |||
FloatType attenuationdB) | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (frequency > 0 && frequency <= sampleRate * 0.5); | |||
jassert (normalizedTransitionWidth > 0 && normalizedTransitionWidth <= 0.5); | |||
jassert (attenuationdB >= -100 && attenuationdB <= 0); | |||
FloatType beta = 0; | |||
if (attenuationdB < -50) | |||
beta = static_cast<FloatType> (0.1102 * (-attenuationdB - 8.7)); | |||
else if (attenuationdB <= 21) | |||
beta = static_cast<FloatType> (0.5842 * std::pow (-attenuationdB - 21, 0.4) + 0.07886 * (-attenuationdB - 21)); | |||
int order = attenuationdB < -21 ? roundDoubleToInt (ceil ((-attenuationdB - 7.95) / (2.285 * normalizedTransitionWidth * 2.0 * double_Pi))) | |||
: roundDoubleToInt (ceil (5.79 / (normalizedTransitionWidth * 2.0 * double_Pi))); | |||
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 normalizedTransitionWidth, FloatType spline) | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (frequency > 0 && frequency <= sampleRate * 0.5); | |||
jassert (normalizedTransitionWidth > 0 && normalizedTransitionWidth <= 0.5); | |||
jassert (spline >= 1.0 && spline <= 4.0); | |||
auto normalizedFrequency = 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) | |||
{ | |||
c[i] = static_cast<FloatType> (2 * normalizedFrequency); | |||
} | |||
else | |||
{ | |||
auto indice = double_Pi * (i - 0.5 * order); | |||
auto indice2 = double_Pi * normalizedTransitionWidth * (i - 0.5 * order) / spline; | |||
c[i] = static_cast<FloatType> (std::sin (2 * indice * normalizedFrequency) | |||
/ 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 normalizedTransitionWidth, | |||
FloatType stopBandWeight) | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (frequency > 0 && frequency <= sampleRate * 0.5); | |||
jassert (normalizedTransitionWidth > 0 && normalizedTransitionWidth <= 0.5); | |||
jassert (stopBandWeight >= 1.0 && stopBandWeight <= 100.0); | |||
auto normalizedFrequency = static_cast<double> (frequency) / sampleRate; | |||
auto wp = 2.0 * double_Pi * (static_cast<double> (normalizedFrequency - normalizedTransitionWidth / 2.0)); | |||
auto ws = 2.0 * double_Pi * (static_cast<double> (normalizedFrequency + normalizedTransitionWidth / 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 * double_Pi) / (double_Pi * x); }; | |||
auto factorp = wp / double_Pi; | |||
auto factors = ws / double_Pi; | |||
for (size_t i = 0; i <= M; ++i) | |||
b (i, 0) = factorp * sinc (factorp * i); | |||
q (0, 0) = factorp + stopBandWeight * (1.0 - factors); | |||
for (size_t i = 1; i <= 2 * M; ++i) | |||
q (i, 0) = factorp * sinc (factorp * i) - stopBandWeight * factors * sinc (factors * 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 * double_Pi) / (double_Pi * x); }; | |||
auto factorp = wp / double_Pi; | |||
auto factors = ws / double_Pi; | |||
for (size_t i = 0; i < M; ++i) | |||
b (i, 0) = factorp * sinc (factorp * (i + 0.5)); | |||
for (size_t i = 0; i < 2 * M; ++i) | |||
{ | |||
qp (i, 0) = 0.25 * factorp * sinc (factorp * i); | |||
qs (i, 0) = -0.25 * stopBandWeight * factors * sinc (factors * 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 normalizedTransitionWidth, | |||
FloatType attenuationdB) | |||
{ | |||
jassert (normalizedTransitionWidth > 0 && normalizedTransitionWidth <= 0.5); | |||
jassert (attenuationdB >= -300 && attenuationdB <= -10); | |||
auto wpT = (0.5 - normalizedTransitionWidth) * double_Pi; | |||
auto n = roundDoubleToInt (ceil ((attenuationdB - 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]; | |||
double NN; | |||
if (n % 2 == 0) | |||
{ | |||
NN = 2.0 * result->getMagnitudeForFrequency (0.5, 1.0); | |||
} | |||
else | |||
{ | |||
auto w01 = std::sqrt (kp * kp + (1 - kp * kp) * std::pow (std::cos (double_Pi / (2.0 * n + 1.0)), 2.0)); | |||
auto om01 = std::acos (-w01); | |||
NN = -2.0 * result->getMagnitudeForFrequency (om01 / (2 * double_Pi), 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> | |||
Array<IIR::Coefficients<FloatType>> | |||
FilterDesign<FloatType>::designIIRLowpassHighOrderButterworthMethod (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB) | |||
{ | |||
return designIIRLowpassHighOrderGeneralMethod (0, frequency, sampleRate, normalizedTransitionWidth, | |||
passbandAttenuationdB, stopbandAttenuationdB); | |||
} | |||
template <typename FloatType> | |||
Array<IIR::Coefficients<FloatType>> | |||
FilterDesign<FloatType>::designIIRLowpassHighOrderChebyshev1Method (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB) | |||
{ | |||
return designIIRLowpassHighOrderGeneralMethod (1, frequency, sampleRate, normalizedTransitionWidth, | |||
passbandAttenuationdB, stopbandAttenuationdB); | |||
} | |||
template <typename FloatType> | |||
Array<IIR::Coefficients<FloatType>> | |||
FilterDesign<FloatType>::designIIRLowpassHighOrderChebyshev2Method (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB) | |||
{ | |||
return designIIRLowpassHighOrderGeneralMethod (2, frequency, sampleRate, normalizedTransitionWidth, | |||
passbandAttenuationdB, stopbandAttenuationdB); | |||
} | |||
template <typename FloatType> | |||
Array<IIR::Coefficients<FloatType>> | |||
FilterDesign<FloatType>::designIIRLowpassHighOrderEllipticMethod (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB) | |||
{ | |||
return designIIRLowpassHighOrderGeneralMethod (3, frequency, sampleRate, normalizedTransitionWidth, | |||
passbandAttenuationdB, stopbandAttenuationdB); | |||
} | |||
template <typename FloatType> | |||
Array<IIR::Coefficients<FloatType>> | |||
FilterDesign<FloatType>::designIIRLowpassHighOrderGeneralMethod (int type, FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB) | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (frequency > 0 && frequency <= sampleRate * 0.5); | |||
jassert (normalizedTransitionWidth > 0 && normalizedTransitionWidth <= 0.5); | |||
jassert (passbandAttenuationdB > -20 && passbandAttenuationdB < 0); | |||
jassert (stopbandAttenuationdB > -300 && stopbandAttenuationdB < -20); | |||
auto normalizedFrequency = frequency / sampleRate; | |||
auto fp = normalizedFrequency - normalizedTransitionWidth / 2; | |||
auto fs = normalizedFrequency + normalizedTransitionWidth / 2; | |||
double Ap = passbandAttenuationdB; | |||
double As = stopbandAttenuationdB; | |||
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 (double_Pi * fp); | |||
auto omegas = std::tan (double_Pi * fs); | |||
auto k = omegap / omegas; | |||
auto k1 = epsp / epss; | |||
int N; | |||
if (type == 0) | |||
{ | |||
N = roundDoubleToInt (ceil (log (1.0 / k1) / log (1.0 / k))); | |||
} | |||
else if (type == 1 || type == 2) | |||
{ | |||
N = roundDoubleToInt (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 = roundDoubleToInt (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 * 0.5 * double_Pi * j)); | |||
} | |||
} | |||
else if (type == 1) | |||
{ | |||
auto v0 = std::asinh (1.0 / epsp) / (0.5 * N * double_Pi); | |||
if (r == 1) | |||
pa.add (-omegap * std::sinh (v0 * 0.5 * double_Pi)); | |||
for (int i = 1; i <= L; ++i) | |||
{ | |||
auto ui = (2 * i - 1.0) / (double) N; | |||
pa.add (omegap * j * std::cos ((ui - j * v0) * 0.5 * double_Pi)); | |||
} | |||
} | |||
else if (type == 2) | |||
{ | |||
auto v0 = std::asinh (epss) / (N * 0.5 * double_Pi); | |||
if (r == 1) | |||
pa.add(-1.0 / (k / omegap * std::sinh (v0 * 0.5 * double_Pi))); | |||
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) * 0.5 * double_Pi))); | |||
za.add (1.0 / (k / omegap * j * std::cos (ui * 0.5 * double_Pi))); | |||
} | |||
} | |||
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])); | |||
} | |||
Array<IIR::Coefficients<FloatType>> theCascadedCoefficients; | |||
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])); | |||
theCascadedCoefficients.add (IIR::Coefficients<FloatType> (b0, b1, 1.f, 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]))); | |||
theCascadedCoefficients.add (IIR::Coefficients<FloatType> (b0, b1, b2, 1, a1, a2)); | |||
} | |||
return theCascadedCoefficients; | |||
} | |||
template <typename FloatType> | |||
typename FilterDesign<FloatType>::IIRPolyphaseAllpassStructure | |||
FilterDesign<FloatType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (FloatType normalizedTransitionWidth, | |||
FloatType stopbandAttenuationdB) | |||
{ | |||
jassert (normalizedTransitionWidth > 0 && normalizedTransitionWidth <= 0.5); | |||
jassert (stopbandAttenuationdB > -300 && stopbandAttenuationdB < -10); | |||
const double wt = 2 * double_Pi * normalizedTransitionWidth; | |||
const double ds = Decibels::decibelsToGain (stopbandAttenuationdB, static_cast<FloatType> (-300.0)); | |||
auto k = std::pow (std::tan ((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 = roundDoubleToInt (ceil (log (k1 * k1 / 16) / 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) * 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 (2 * m * double_Pi * 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 (IIR::Coefficients<FloatType> (static_cast<FloatType> (ai[i]), | |||
0, 1, 1, 0, static_cast<FloatType> (ai[i]))); | |||
structure.delayedPath.add (IIR::Coefficients<FloatType> (0, 1, 1, 0)); | |||
for (int i = 1; i < N; i += 2) | |||
structure.delayedPath.add (IIR::Coefficients<FloatType> (static_cast<FloatType> (ai[i]), | |||
0, 1, 1, 0, static_cast<FloatType> (ai[i]))); | |||
return structure; | |||
} | |||
template struct FilterDesign<float>; | |||
template struct FilterDesign<double>; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,266 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 lowpass filters. They can be used | |||
for processing directly audio as an equalizer, in resampling algorithms etc. | |||
see FIRFilter::Coefficients, FIRFilter, WindowingFunction, IIRFilter::Coefficients, IIRFilter | |||
*/ | |||
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 normalized 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 an attenuation 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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param attenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static FIRCoefficientsPtr designFIRLowpassKaiserMethod (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType attenuationdB); | |||
/** 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 normalizedTransitionWidth the normalized 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 normalizedTransitionWidth, | |||
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 normalizedTransitionWidth the normalized 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 normalizedTransitionWidth, | |||
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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param attenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static FIRCoefficientsPtr designFIRLowpassHalfBandEquirippleMethod (FloatType normalizedTransitionWidth, | |||
FloatType attenuationdB); | |||
//============================================================================== | |||
/** This method returns an array of IIR::Coefficients, made to be used in | |||
cascaded IIRFilters, providing a minimum phase lowpass 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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param passbandAttenuationdB the lowest attenuation in dB expected in the pass band | |||
@param stopbandAttenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static Array<IIRCoefficients> designIIRLowpassHighOrderButterworthMethod (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB); | |||
/** This method returns an array of IIR::Coefficients, made to be used in | |||
cascaded IIRFilters, providing a minimum phase lowpass 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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param passbandAttenuationdB the lowest attenuation in dB expected in the pass band | |||
@param stopbandAttenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static Array<IIRCoefficients> designIIRLowpassHighOrderChebyshev1Method (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB); | |||
/** This method returns an array of IIR::Coefficients, made to be used in | |||
cascaded IIRFilters, providing a minimum phase lowpass 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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param passbandAttenuationdB the lowest attenuation in dB expected in the pass band | |||
@param stopbandAttenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static Array<IIRCoefficients> designIIRLowpassHighOrderChebyshev2Method (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB); | |||
/** This method returns an array of IIR::Coefficients, made to be used in | |||
cascaded IIR::Filters, providing a minimum phase lowpass 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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param passbandAttenuationdB the lowest attenuation in dB expected in the pass band | |||
@param stopbandAttenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static Array<IIRCoefficients> designIIRLowpassHighOrderEllipticMethod (FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB); | |||
/** The structure returned by the function designIIRLowpassHalfBandPolyphaseAllpassMethod. | |||
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. | |||
*/ | |||
struct IIRPolyphaseAllpassStructure { Array<IIRCoefficients> directPath, delayedPath; }; | |||
/** 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 normalizedTransitionWidth the normalized size between 0 and 0.5 of the transition | |||
between the pass band and the stop band | |||
@param stopbandAttenuationdB the attenuation in dB expected in the stop band | |||
*/ | |||
static IIRPolyphaseAllpassStructure designIIRLowpassHalfBandPolyphaseAllpassMethod (FloatType normalizedTransitionWidth, | |||
FloatType stopbandAttenuationdB); | |||
private: | |||
//============================================================================== | |||
static Array<double> getPartialImpulseResponseHn (int n, double kp); | |||
static Array<IIRCoefficients> designIIRLowpassHighOrderGeneralMethod (int type, FloatType frequency, double sampleRate, | |||
FloatType normalizedTransitionWidth, | |||
FloatType passbandAttenuationdB, | |||
FloatType stopbandAttenuationdB); | |||
FilterDesign() = delete; | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,145 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 stereo uniform-partitioned convolution of an input signal with an | |||
impulse response in the frequency domain, using the juce FFT class. | |||
It provides some thread-safe functions to load impulse responses as well, | |||
from audio files or memory on the fly without any noticeable artefacts, | |||
performing resampling and trimming if necessary. | |||
The processing is equivalent to the time domain convolution done in the | |||
class FIRFilter, with a FIRFilter::Coefficients object having as | |||
coefficients the samples of the impulse response. However, it is more | |||
efficient in general to do frequency domain convolution when the size of | |||
the impulse response is higher than 64 samples. | |||
@see FIRFilter, FIRFilter::Coefficients, FFT | |||
*/ | |||
class JUCE_API Convolution | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Initialises an object for performing convolution in the frequency domain. */ | |||
Convolution(); | |||
/** Destructor. */ | |||
~Convolution(); | |||
//============================================================================== | |||
/** Must be called before loading any impulse response, to provide to the | |||
convolution the maximumBufferSize to handle, and the sample rate useful for | |||
optional resampling. | |||
*/ | |||
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> | |||
void process (const ProcessContext& context) noexcept | |||
{ | |||
static_assert (std::is_same<typename ProcessContext::SampleType, float>::value, | |||
"Convolution engine only supports single precision floating point data"); | |||
processSamples (context.getInputBlock(), context.getOutputBlock(), context.isBypassed); | |||
} | |||
//============================================================================== | |||
/** 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 : obviously, don't try to use this function on float samples, since the | |||
data is supposed to be an audio file in its binary format, and be sure that | |||
the original data is not going to move at all its memory location during the | |||
process !! | |||
@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 wantsStereo requests to load both stereo channels or only one mono channel | |||
@param wantsTrimming requests to trim the start and the end of the impulse response | |||
@param size the expected size for the impulse response after loading | |||
*/ | |||
void loadImpulseResponse (const void* sourceData, size_t sourceDataSize, | |||
bool wantsStereo, bool wantsTrimming, size_t size); | |||
/** This function loads an impulse response from an audio file on any drive. 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 wantsStereo requests to load both stereo channels or only one mono channel | |||
@param wantsTrimming requests to trim the start and the end of the impulse response | |||
@param size the expected size for the impulse response after loading | |||
*/ | |||
void loadImpulseResponse (const File& fileImpulseResponse, | |||
bool wantsStereo, bool wantsTrimming, size_t size); | |||
/** This function loads an impulse response from an audio buffer, which is | |||
copied before doing anything else. Performs some resampling and | |||
pre-processing as well if needed. | |||
@param buffer the AudioBuffer to use | |||
@param bufferSampleRate the sampleRate of the data in the AudioBuffer | |||
@param wantsStereo requests to load both stereo channels or only one mono channel | |||
@param wantsTrimming requests to trim the start and the end of the impulse response | |||
@param size the expected size for the impulse response after loading | |||
*/ | |||
void copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>& buffer, double bufferSampleRate, | |||
bool wantsStereo, bool wantsTrimming, size_t size); | |||
private: | |||
//============================================================================== | |||
struct Pimpl; | |||
ScopedPointer<Pimpl> pimpl; | |||
//============================================================================== | |||
void processSamples (const AudioBlock<float>&, AudioBlock<float>&, bool isBypassed) noexcept; | |||
//============================================================================== | |||
double sampleRate; | |||
bool currentIsBypassed = false; | |||
LinearSmoothedValue<float> volumeDry[2], volumeWet[2]; | |||
AudioBlock<float> dryBuffer; | |||
HeapBlock<char> dryBufferStorage; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Convolution) | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,845 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 FFT::Instance | |||
{ | |||
virtual ~Instance() {} | |||
virtual void perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept = 0; | |||
virtual void performRealOnlyForwardTransform (float*, bool) const noexcept = 0; | |||
virtual void performRealOnlyInverseTransform (float*) const noexcept = 0; | |||
}; | |||
struct FFT::Engine | |||
{ | |||
Engine (int priorityToUse) : enginePriority (priorityToUse) | |||
{ | |||
EnginePriorityComparator comparator; | |||
getEngines().addSorted (comparator, this); | |||
} | |||
virtual ~Engine() {} | |||
virtual FFT::Instance* create (int order) const = 0; | |||
//============================================================================== | |||
static FFT::Instance* createBestEngineForPlatform (int order) | |||
{ | |||
for (auto* engine : getEngines()) | |||
if (auto* instance = engine->create (order)) | |||
return instance; | |||
jassertfalse; // This should never happen as the fallback engine should always work! | |||
return nullptr; | |||
} | |||
private: | |||
struct EnginePriorityComparator | |||
{ | |||
static int compareElements (Engine* first, Engine* second) noexcept | |||
{ | |||
// sort in reverse order | |||
return DefaultElementComparator<int>::compareElements (second->enginePriority, first->enginePriority); | |||
} | |||
}; | |||
static Array<Engine*>& getEngines() | |||
{ | |||
static Array<Engine*> engines; | |||
return engines; | |||
} | |||
int enginePriority; // used so that faster engines have priority over slower ones | |||
}; | |||
template <typename InstanceToUse> | |||
struct FFT::EngineImpl : public FFT::Engine | |||
{ | |||
EngineImpl() : FFT::Engine (InstanceToUse::priority) {} | |||
FFT::Instance* create (int order) const override { return InstanceToUse::create (order); } | |||
}; | |||
//============================================================================== | |||
//============================================================================== | |||
struct FFTFallback : public FFT::Instance | |||
{ | |||
// this should have the least priority of all engines | |||
static constexpr int priority = -1; | |||
static FFTFallback* create (int order) | |||
{ | |||
return new FFTFallback (order); | |||
} | |||
FFTFallback (int order) | |||
{ | |||
configForward = new FFTConfig (1 << order, false); | |||
configInverse = new FFTConfig (1 << order, true); | |||
size = 1 << order; | |||
} | |||
void perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept override | |||
{ | |||
if (size == 1) | |||
{ | |||
*output = *input; | |||
return; | |||
} | |||
const SpinLock::ScopedLockType sl(processLock); | |||
jassert (configForward != nullptr); | |||
if (inverse) | |||
{ | |||
configInverse->perform (input, output); | |||
const float scaleFactor = 1.0f / size; | |||
for (int i = 0; i < size; ++i) | |||
output[i] *= scaleFactor; | |||
} | |||
else | |||
{ | |||
configForward->perform (input, output); | |||
} | |||
} | |||
const size_t maxFFTScratchSpaceToAlloca = 256 * 1024; | |||
void performRealOnlyForwardTransform (float* d, bool) const noexcept override | |||
{ | |||
if (size == 1) | |||
return; | |||
const size_t scratchSize = 16 + sizeof (Complex<float>) * (size_t) size; | |||
if (scratchSize < maxFFTScratchSpaceToAlloca) | |||
{ | |||
performRealOnlyForwardTransform (static_cast<Complex<float>*> (alloca (scratchSize)), d); | |||
} | |||
else | |||
{ | |||
HeapBlock<char> heapSpace (scratchSize); | |||
performRealOnlyForwardTransform (reinterpret_cast<Complex<float>*> (heapSpace.getData()), d); | |||
} | |||
} | |||
void performRealOnlyInverseTransform (float* d) const noexcept override | |||
{ | |||
if (size == 1) | |||
return; | |||
const size_t scratchSize = 16 + sizeof (Complex<float>) * (size_t) size; | |||
if (scratchSize < maxFFTScratchSpaceToAlloca) | |||
{ | |||
performRealOnlyInverseTransform (static_cast<Complex<float>*> (alloca (scratchSize)), d); | |||
} | |||
else | |||
{ | |||
HeapBlock<char> heapSpace (scratchSize); | |||
performRealOnlyInverseTransform (reinterpret_cast<Complex<float>*> (heapSpace.getData()), d); | |||
} | |||
} | |||
void performRealOnlyForwardTransform (Complex<float>* scratch, float* d) const noexcept | |||
{ | |||
for (int i = 0; i < size; ++i) | |||
{ | |||
scratch[i].real (d[i]); | |||
scratch[i].imag (0); | |||
} | |||
perform (scratch, reinterpret_cast<Complex<float>*> (d), false); | |||
} | |||
void performRealOnlyInverseTransform (Complex<float>* scratch, float* d) const noexcept | |||
{ | |||
auto* input = reinterpret_cast<Complex<float>*> (d); | |||
for (auto i = size >> 1; i < size; ++i) | |||
input[i] = std::conj (input[size - i]); | |||
perform (input, scratch, true); | |||
for (int i = 0; i < size; ++i) | |||
{ | |||
d[i] = scratch[i].real(); | |||
d[i + size] = scratch[i].imag(); | |||
} | |||
} | |||
//============================================================================== | |||
struct FFTConfig | |||
{ | |||
FFTConfig (int sizeOfFFT, bool isInverse) | |||
: fftSize (sizeOfFFT), inverse (isInverse), twiddleTable ((size_t) sizeOfFFT) | |||
{ | |||
const double inverseFactor = (inverse ? 2.0 : -2.0) * double_Pi / (double) fftSize; | |||
if (fftSize <= 4) | |||
{ | |||
for (int i = 0; i < fftSize; ++i) | |||
{ | |||
const double phase = i * inverseFactor; | |||
twiddleTable[i].real ((float) std::cos (phase)); | |||
twiddleTable[i].imag ((float) std::sin (phase)); | |||
} | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < fftSize / 4; ++i) | |||
{ | |||
const double phase = i * inverseFactor; | |||
twiddleTable[i].real ((float) std::cos (phase)); | |||
twiddleTable[i].imag ((float) std::sin (phase)); | |||
} | |||
for (int i = fftSize / 4; i < fftSize / 2; ++i) | |||
{ | |||
const int index = i - fftSize / 4; | |||
twiddleTable[i].real (inverse ? -twiddleTable[index].imag() : twiddleTable[index].imag()); | |||
twiddleTable[i].imag (inverse ? twiddleTable[index].real() : -twiddleTable[index].real()); | |||
} | |||
twiddleTable[fftSize / 2].real (-1.0f); | |||
twiddleTable[fftSize / 2].imag (0.0f); | |||
for (int i = fftSize / 2; i < fftSize; ++i) | |||
{ | |||
const int index = fftSize / 2 - (i - fftSize / 2); | |||
twiddleTable[i] = conj(twiddleTable[index]); | |||
} | |||
} | |||
const int root = (int) std::sqrt ((double) fftSize); | |||
int divisor = 4, n = fftSize; | |||
for (int i = 0; i < numElementsInArray (factors); ++i) | |||
{ | |||
while ((n % divisor) != 0) | |||
{ | |||
if (divisor == 2) divisor = 3; | |||
else if (divisor == 4) divisor = 2; | |||
else divisor += 2; | |||
if (divisor > root) | |||
divisor = n; | |||
} | |||
n /= divisor; | |||
jassert (divisor == 1 || divisor == 2 || divisor == 4); | |||
factors[i].radix = divisor; | |||
factors[i].length = n; | |||
} | |||
} | |||
void perform (const Complex<float>* input, Complex<float>* output) const noexcept | |||
{ | |||
perform (input, output, 1, 1, factors); | |||
} | |||
const int fftSize; | |||
const bool inverse; | |||
struct Factor { int radix, length; }; | |||
Factor factors[32]; | |||
HeapBlock<Complex<float>> twiddleTable; | |||
void perform (const Complex<float>* input, Complex<float>* output, int stride, int strideIn, const Factor* facs) const noexcept | |||
{ | |||
auto factor = *facs++; | |||
auto* originalOutput = output; | |||
auto* outputEnd = output + factor.radix * factor.length; | |||
if (stride == 1 && factor.radix <= 5) | |||
{ | |||
for (int i = 0; i < factor.radix; ++i) | |||
perform (input + stride * strideIn * i, output + i * factor.length, stride * factor.radix, strideIn, facs); | |||
butterfly (factor, output, stride); | |||
return; | |||
} | |||
if (factor.length == 1) | |||
{ | |||
do | |||
{ | |||
*output++ = *input; | |||
input += stride * strideIn; | |||
} | |||
while (output < outputEnd); | |||
} | |||
else | |||
{ | |||
do | |||
{ | |||
perform (input, output, stride * factor.radix, strideIn, facs); | |||
input += stride * strideIn; | |||
output += factor.length; | |||
} | |||
while (output < outputEnd); | |||
} | |||
butterfly (factor, originalOutput, stride); | |||
} | |||
void butterfly (const Factor factor, Complex<float>* data, int stride) const noexcept | |||
{ | |||
switch (factor.radix) | |||
{ | |||
case 1: break; | |||
case 2: butterfly2 (data, stride, factor.length); return; | |||
case 4: butterfly4 (data, stride, factor.length); return; | |||
default: jassertfalse; break; | |||
} | |||
auto* scratch = static_cast<Complex<float>*> (alloca (sizeof (Complex<float>) * (size_t) factor.radix)); | |||
for (int i = 0; i < factor.length; ++i) | |||
{ | |||
for (int k = i, q1 = 0; q1 < factor.radix; ++q1) | |||
{ | |||
scratch[q1] = data[k]; | |||
k += factor.length; | |||
} | |||
for (int k = i, q1 = 0; q1 < factor.radix; ++q1) | |||
{ | |||
int twiddleIndex = 0; | |||
data[k] = scratch[0]; | |||
for (int q = 1; q < factor.radix; ++q) | |||
{ | |||
twiddleIndex += stride * k; | |||
if (twiddleIndex >= fftSize) | |||
twiddleIndex -= fftSize; | |||
data[k] += scratch[q] * twiddleTable[twiddleIndex]; | |||
} | |||
k += factor.length; | |||
} | |||
} | |||
} | |||
void butterfly2 (Complex<float>* data, const int stride, const int length) const noexcept | |||
{ | |||
auto* dataEnd = data + length; | |||
auto* tw = twiddleTable.getData(); | |||
for (int i = length; --i >= 0;) | |||
{ | |||
auto s = *dataEnd; | |||
s *= (*tw); | |||
tw += stride; | |||
*dataEnd++ = *data - s; | |||
*data++ += s; | |||
} | |||
} | |||
void butterfly4 (Complex<float>* data, const int stride, const int length) const noexcept | |||
{ | |||
auto lengthX2 = length * 2; | |||
auto lengthX3 = length * 3; | |||
auto strideX2 = stride * 2; | |||
auto strideX3 = stride * 3; | |||
auto* twiddle1 = twiddleTable.getData(); | |||
auto* twiddle2 = twiddle1; | |||
auto* twiddle3 = twiddle1; | |||
for (int i = length; --i >= 0;) | |||
{ | |||
auto s0 = data[length] * *twiddle1; | |||
auto s1 = data[lengthX2] * *twiddle2; | |||
auto s2 = data[lengthX3] * *twiddle3; | |||
auto s3 = s0; s3 += s2; | |||
auto s4 = s0; s4 -= s2; | |||
auto s5 = *data; s5 -= s1; | |||
*data += s1; | |||
data[lengthX2] = *data; | |||
data[lengthX2] -= s3; | |||
twiddle1 += stride; | |||
twiddle2 += strideX2; | |||
twiddle3 += strideX3; | |||
*data += s3; | |||
if (inverse) | |||
{ | |||
data[length].real (s5.real() - s4.imag()); | |||
data[length].imag (s5.imag() + s4.real()); | |||
data[lengthX3].real (s5.real() + s4.imag()); | |||
data[lengthX3].imag (s5.imag() - s4.real()); | |||
} | |||
else | |||
{ | |||
data[length].real (s5.real() + s4.imag()); | |||
data[length].imag (s5.imag() - s4.real()); | |||
data[lengthX3].real (s5.real() - s4.imag()); | |||
data[lengthX3].imag (s5.imag() + s4.real()); | |||
} | |||
++data; | |||
} | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFTConfig) | |||
}; | |||
//============================================================================== | |||
SpinLock processLock; | |||
ScopedPointer<FFTConfig> configForward, configInverse; | |||
int size; | |||
}; | |||
FFT::EngineImpl<FFTFallback> fftFallback; | |||
//============================================================================== | |||
//============================================================================== | |||
#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK | |||
struct AppleFFT : public FFT::Instance | |||
{ | |||
static constexpr int priority = 5; | |||
static AppleFFT* create (int order) | |||
{ | |||
return new AppleFFT (order); | |||
} | |||
AppleFFT (int orderToUse) | |||
: order (static_cast<vDSP_Length> (orderToUse)), | |||
fftSetup (vDSP_create_fftsetup (order, 2)), | |||
forwardNormalisation (.5f), | |||
inverseNormalisation (1.0f / static_cast<float> (1 << order)) | |||
{} | |||
~AppleFFT() override | |||
{ | |||
if (fftSetup != nullptr) | |||
{ | |||
vDSP_destroy_fftsetup (fftSetup); | |||
fftSetup = nullptr; | |||
} | |||
} | |||
void perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept override | |||
{ | |||
auto size = (1 << order); | |||
DSPSplitComplex splitInput (toSplitComplex (const_cast<Complex<float>*> (input))); | |||
DSPSplitComplex splitOutput (toSplitComplex (output)); | |||
vDSP_fft_zop (fftSetup, &splitInput, 2, &splitOutput, 2, | |||
order, inverse ? kFFTDirection_Inverse : kFFTDirection_Forward); | |||
float factor = (inverse ? inverseNormalisation : forwardNormalisation * 2.0f); | |||
vDSP_vsmul ((float*) output, 1, &factor, (float*) output, 1, static_cast<size_t> (size << 1)); | |||
} | |||
void performRealOnlyForwardTransform (float* inoutData, bool ignoreNegativeFreqs) const noexcept override | |||
{ | |||
auto size = (1 << order); | |||
auto* inout = reinterpret_cast<Complex<float>*> (inoutData); | |||
auto splitInOut (toSplitComplex (inout)); | |||
inoutData[size] = 0.0f; | |||
vDSP_fft_zrip (fftSetup, &splitInOut, 2, order, kFFTDirection_Forward); | |||
vDSP_vsmul (inoutData, 1, &forwardNormalisation, inoutData, 1, static_cast<size_t> (size << 1)); | |||
mirrorResult (inout, ignoreNegativeFreqs); | |||
} | |||
void performRealOnlyInverseTransform (float* inoutData) const noexcept override | |||
{ | |||
auto* inout = reinterpret_cast<Complex<float>*> (inoutData); | |||
auto size = (1 << order); | |||
auto splitInOut (toSplitComplex (inout)); | |||
// Imaginary part of nyquist and DC frequencies are always zero | |||
// so Apple uses the imaginary part of the DC frequency to store | |||
// the real part of the nyquist frequency | |||
if (size != 1) | |||
inout[0] = Complex<float> (inout[0].real(), inout[size >> 1].real()); | |||
vDSP_fft_zrip (fftSetup, &splitInOut, 2, order, kFFTDirection_Inverse); | |||
vDSP_vsmul (inoutData, 1, &inverseNormalisation, inoutData, 1, static_cast<size_t> (size << 1)); | |||
vDSP_vclr (inoutData + size, 1, static_cast<size_t> (size)); | |||
} | |||
private: | |||
//============================================================================== | |||
void mirrorResult (Complex<float>* out, bool ignoreNegativeFreqs) const noexcept | |||
{ | |||
auto size = (1 << order); | |||
auto i = size >> 1; | |||
// Imaginary part of nyquist and DC frequencies are always zero | |||
// so Apple uses the imaginary part of the DC frequency to store | |||
// the real part of the nyquist frequency | |||
out[i++] = { out[0].imag(), 0.0 }; | |||
out[0] = { out[0].real(), 0.0 }; | |||
if (! ignoreNegativeFreqs) | |||
for (; i < size; ++i) | |||
out[i] = std::conj (out[size - i]); | |||
} | |||
static DSPSplitComplex toSplitComplex (Complex<float>* data) noexcept | |||
{ | |||
// this assumes that Complex interleaves real and imaginary parts | |||
// and is tightly packed. | |||
return { reinterpret_cast<float*> (data), | |||
reinterpret_cast<float*> (data) + 1}; | |||
} | |||
//============================================================================== | |||
vDSP_Length order; | |||
FFTSetup fftSetup; | |||
float forwardNormalisation, inverseNormalisation; | |||
}; | |||
FFT::EngineImpl<AppleFFT> appleFFT; | |||
#endif | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_DSP_USE_SHARED_FFTW || JUCE_DSP_USE_STATIC_FFTW | |||
#if JUCE_DSP_USE_STATIC_FFTW | |||
extern "C" | |||
{ | |||
void* fftwf_plan_dft_1d (int, void*, void*, int, int); | |||
void* fftwf_plan_dft_r2c_1d (int, void*, void*, int); | |||
void* fftwf_plan_dft_c2r_1d (int, void*, void*, int); | |||
void fftwf_destroy_plan (void*); | |||
void fftwf_execute_dft (void*, void*, void*); | |||
void fftwf_execute_dft_r2c (void*, void*, void*); | |||
void fftwf_execute_dft_c2r (void*, void*, void*); | |||
} | |||
#endif | |||
struct FFTWImpl : public FFT::Instance | |||
{ | |||
#if JUCE_DSP_USE_STATIC_FFTW | |||
// if the JUCE developer has gone through the hassle of statically | |||
// linking in fftw, they probably want to use it | |||
static constexpr int priority = 10; | |||
#else | |||
static constexpr int priority = 3; | |||
#endif | |||
struct FFTWPlan; | |||
using FFTWPlanRef = FFTWPlan*; | |||
enum | |||
{ | |||
measure = 0, | |||
unaligned = (1 << 1), | |||
estimate = (1 << 6) | |||
}; | |||
struct Symbols | |||
{ | |||
FFTWPlanRef (*plan_dft_fftw) (unsigned, Complex<float>*, Complex<float>*, int, unsigned); | |||
FFTWPlanRef (*plan_r2c_fftw) (unsigned, float*, Complex<float>*, unsigned); | |||
FFTWPlanRef (*plan_c2r_fftw) (unsigned, Complex<float>*, float*, unsigned); | |||
void (*destroy_fftw) (FFTWPlanRef); | |||
void (*execute_dft_fftw) (FFTWPlanRef, const Complex<float>*, Complex<float>*); | |||
void (*execute_r2c_fftw) (FFTWPlanRef, float*, Complex<float>*); | |||
void (*execute_c2r_fftw) (FFTWPlanRef, Complex<float>*, float*); | |||
#if JUCE_DSP_USE_STATIC_FFTW | |||
template <typename FuncPtr, typename ActualSymbolType> | |||
static bool symbol (FuncPtr& dst, ActualSymbolType sym) | |||
{ | |||
dst = reinterpret_cast<FuncPtr> (sym); | |||
return true; | |||
} | |||
#else | |||
template <typename FuncPtr> | |||
static bool symbol (DynamicLibrary& lib, FuncPtr& dst, const char* name) | |||
{ | |||
dst = reinterpret_cast<FuncPtr> (lib.getFunction (name)); | |||
return (dst != nullptr); | |||
} | |||
#endif | |||
}; | |||
static FFTWImpl* create (int order) | |||
{ | |||
DynamicLibrary lib; | |||
#if ! JUCE_DSP_USE_STATIC_FFTW | |||
#if JUCE_MAC | |||
const char* libsuffix = "dylib"; | |||
#elif JUCE_WINDOWS | |||
const char* libsuffix = "dll"; | |||
#else | |||
const char* libsuffix = "so"; | |||
#endif | |||
if (lib.open (String ("libfftw3f.") + libsuffix)) | |||
#endif | |||
{ | |||
Symbols symbols; | |||
#if JUCE_DSP_USE_STATIC_FFTW | |||
if (! Symbols::symbol (symbols.plan_dft_fftw, fftwf_plan_dft_1d)) return nullptr; | |||
if (! Symbols::symbol (symbols.plan_r2c_fftw, fftwf_plan_dft_r2c_1d)) return nullptr; | |||
if (! Symbols::symbol (symbols.plan_c2r_fftw, fftwf_plan_dft_c2r_1d)) return nullptr; | |||
if (! Symbols::symbol (symbols.destroy_fftw, fftwf_destroy_plan)) return nullptr; | |||
if (! Symbols::symbol (symbols.execute_dft_fftw, fftwf_execute_dft)) return nullptr; | |||
if (! Symbols::symbol (symbols.execute_r2c_fftw, fftwf_execute_dft_r2c)) return nullptr; | |||
if (! Symbols::symbol (symbols.execute_c2r_fftw, fftwf_execute_dft_c2r)) return nullptr; | |||
#else | |||
if (! Symbols::symbol (lib, symbols.plan_dft_fftw, "fftwf_plan_dft_1d")) return nullptr; | |||
if (! Symbols::symbol (lib, symbols.plan_r2c_fftw, "fftwf_plan_dft_r2c_1d")) return nullptr; | |||
if (! Symbols::symbol (lib, symbols.plan_c2r_fftw, "fftwf_plan_dft_c2r_1d")) return nullptr; | |||
if (! Symbols::symbol (lib, symbols.destroy_fftw, "fftwf_destroy_plan")) return nullptr; | |||
if (! Symbols::symbol (lib, symbols.execute_dft_fftw, "fftwf_execute_dft")) return nullptr; | |||
if (! Symbols::symbol (lib, symbols.execute_r2c_fftw, "fftwf_execute_dft_r2c")) return nullptr; | |||
if (! Symbols::symbol (lib, symbols.execute_c2r_fftw, "fftwf_execute_dft_c2r")) return nullptr; | |||
#endif | |||
return new FFTWImpl (static_cast<size_t> (order), static_cast<DynamicLibrary&&> (lib), symbols); | |||
} | |||
return nullptr; | |||
} | |||
FFTWImpl (size_t orderToUse, DynamicLibrary&& libraryToUse, const Symbols& symbols) | |||
: fftwLibrary (std::move (libraryToUse)), fftw (symbols), order (static_cast<size_t> (orderToUse)) | |||
{ | |||
auto n = (1u << order); | |||
HeapBlock<Complex<float>> in (n), out (n); | |||
c2cForward = fftw.plan_dft_fftw (n, in.getData(), out.getData(), -1, unaligned | estimate); | |||
c2cInverse = fftw.plan_dft_fftw (n, in.getData(), out.getData(), +1, unaligned | estimate); | |||
r2c = fftw.plan_r2c_fftw (n, (float*) in.getData(), in.getData(), unaligned | estimate); | |||
c2r = fftw.plan_c2r_fftw (n, in.getData(), (float*) in.getData(), unaligned | estimate); | |||
} | |||
~FFTWImpl() override | |||
{ | |||
fftw.destroy_fftw (c2cForward); | |||
fftw.destroy_fftw (c2cInverse); | |||
fftw.destroy_fftw (r2c); | |||
fftw.destroy_fftw (c2r); | |||
} | |||
void perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept override | |||
{ | |||
if (inverse) | |||
{ | |||
auto n = (1u << order); | |||
fftw.execute_dft_fftw (c2cInverse, input, output); | |||
FloatVectorOperations::multiply ((float*) output, 1.0f / static_cast<float> (n), (int) n << 1); | |||
} | |||
else | |||
{ | |||
fftw.execute_dft_fftw (c2cForward, input, output); | |||
} | |||
} | |||
void performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept override | |||
{ | |||
if (order == 0) | |||
return; | |||
auto* out = reinterpret_cast<Complex<float>*> (inputOutputData); | |||
fftw.execute_r2c_fftw (r2c, inputOutputData, out); | |||
auto size = (1 << order); | |||
if (! ignoreNegativeFreqs) | |||
for (auto i = size >> 1; i < size; ++i) | |||
out[i] = std::conj (out[size - i]); | |||
} | |||
void performRealOnlyInverseTransform (float* inputOutputData) const noexcept override | |||
{ | |||
auto n = (1u << order); | |||
fftw.execute_c2r_fftw (c2r, (Complex<float>*) inputOutputData, inputOutputData); | |||
FloatVectorOperations::multiply ((float*) inputOutputData, 1.0f / static_cast<float> (n), (int) n); | |||
} | |||
//============================================================================== | |||
DynamicLibrary fftwLibrary; | |||
Symbols fftw; | |||
size_t order; | |||
FFTWPlanRef c2cForward, c2cInverse, r2c, c2r; | |||
}; | |||
FFT::EngineImpl<FFTWImpl> fftwEngine; | |||
#endif | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_DSP_USE_INTEL_MKL | |||
struct IntelFFT : public FFT::Instance | |||
{ | |||
static constexpr int priority = 8; | |||
static bool succeeded (MKL_LONG status) noexcept { return status == 0; } | |||
static IntelFFT* create (int orderToUse) | |||
{ | |||
DFTI_DESCRIPTOR_HANDLE mklc2c, mklc2r; | |||
if (DftiCreateDescriptor (&mklc2c, DFTI_SINGLE, DFTI_COMPLEX, 1, 1 << orderToUse) == 0) | |||
{ | |||
if (succeeded (DftiSetValue (mklc2c, DFTI_PLACEMENT, DFTI_NOT_INPLACE)) | |||
&& succeeded (DftiSetValue (mklc2c, DFTI_BACKWARD_SCALE, 1.0f / static_cast<float> (1 << orderToUse))) | |||
&& succeeded (DftiCommitDescriptor (mklc2c))) | |||
{ | |||
if (succeeded (DftiCreateDescriptor (&mklc2r, DFTI_SINGLE, DFTI_REAL, 1, 1 << orderToUse))) | |||
{ | |||
if (succeeded (DftiSetValue (mklc2r, DFTI_PLACEMENT, DFTI_INPLACE)) | |||
&& succeeded (DftiSetValue (mklc2r, DFTI_BACKWARD_SCALE, 1.0f / static_cast<float> (1 << orderToUse))) | |||
&& succeeded (DftiCommitDescriptor (mklc2r))) | |||
{ | |||
return new IntelFFT (static_cast<size_t> (orderToUse), mklc2c, mklc2r); | |||
} | |||
DftiFreeDescriptor (&mklc2r); | |||
} | |||
} | |||
DftiFreeDescriptor (&mklc2c); | |||
} | |||
return {}; | |||
} | |||
IntelFFT (size_t orderToUse, DFTI_DESCRIPTOR_HANDLE c2cToUse, DFTI_DESCRIPTOR_HANDLE cr2ToUse) | |||
: order (orderToUse), c2c (c2cToUse), c2r (cr2ToUse) | |||
{} | |||
~IntelFFT() | |||
{ | |||
DftiFreeDescriptor (&c2c); | |||
DftiFreeDescriptor (&c2r); | |||
} | |||
void perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept override | |||
{ | |||
if (inverse) | |||
DftiComputeBackward (c2c, (void*) input, output); | |||
else | |||
DftiComputeForward (c2c, (void*) input, output); | |||
} | |||
void performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept override | |||
{ | |||
if (order == 0) | |||
return; | |||
DftiComputeForward (c2r, inputOutputData); | |||
auto* out = reinterpret_cast<Complex<float>*> (inputOutputData); | |||
auto size = (1 << order); | |||
if (! ignoreNegativeFreqs) | |||
for (auto i = size >> 1; i < size; ++i) | |||
out[i] = std::conj (out[size - i]); | |||
} | |||
void performRealOnlyInverseTransform (float* inputOutputData) const noexcept override | |||
{ | |||
DftiComputeBackward (c2r, inputOutputData); | |||
} | |||
size_t order; | |||
DFTI_DESCRIPTOR_HANDLE c2c, c2r; | |||
}; | |||
FFT::EngineImpl<IntelFFT> fftwEngine; | |||
#endif | |||
//============================================================================== | |||
//============================================================================== | |||
FFT::FFT (int order) | |||
: engine (FFT::Engine::createBestEngineForPlatform (order)), | |||
size (1 << order) | |||
{} | |||
FFT::~FFT() {} | |||
void FFT::perform (const Complex<float>* input, Complex<float>* output, bool inverse) const noexcept | |||
{ | |||
if (engine != nullptr) | |||
engine->perform (input, output, inverse); | |||
} | |||
void FFT::performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNeagtiveFreqs) const noexcept | |||
{ | |||
if (engine != nullptr) | |||
engine->performRealOnlyForwardTransform (inputOutputData, ignoreNeagtiveFreqs); | |||
} | |||
void FFT::performRealOnlyInverseTransform (float* inputOutputData) const noexcept | |||
{ | |||
if (engine != nullptr) | |||
engine->performRealOnlyInverseTransform (inputOutputData); | |||
} | |||
void FFT::performFrequencyOnlyForwardTransform (float* inputOutputData) const noexcept | |||
{ | |||
if (size == 1) | |||
return; | |||
performRealOnlyForwardTransform (inputOutputData); | |||
auto* out = reinterpret_cast<Complex<float>*> (inputOutputData); | |||
for (auto i = 0; i < size; ++i) | |||
inputOutputData[i] = std::abs (out[i]); | |||
zeromem (&inputOutputData[size], sizeof (float) * static_cast<size_t> (size)); | |||
} | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,120 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
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); | |||
/** 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 frequences (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; | |||
ScopedPointer<Instance> engine; | |||
int size; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFT) | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,213 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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") {} | |||
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 reverve) | |||
{ | |||
float base_freq = static_cast<float>(((reverve ? 1.0 : -1.0) * 2.0 * double_Pi) | |||
/ 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 reverve) | |||
{ | |||
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>(((reverve ? 1.0 : -1.0) * 2.0 * double_Pi) | |||
/ 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) * (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 |
@@ -0,0 +1,194 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 inline 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 normalize, FloatType beta) | |||
{ | |||
fillWindowingTables (size, type, normalize, beta); | |||
} | |||
template <typename FloatType> | |||
void WindowingFunction<FloatType>::fillWindowingTables (size_t size, WindowingMethod type, | |||
bool normalize, FloatType beta) noexcept | |||
{ | |||
windowTable.resize (static_cast<int> (size)); | |||
fillWindowingTables (windowTable.getRawDataPointer(), size, type, normalize, beta); | |||
} | |||
template <typename FloatType> | |||
void WindowingFunction<FloatType>::fillWindowingTables (FloatType* samples, size_t size, | |||
WindowingMethod type, bool normalize, | |||
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::fabs ((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); | |||
for (size_t i = 0; i < size; ++i) | |||
samples[i] = static_cast<FloatType> (SpecialFunctions::besselI0 (beta * std::sqrt (1.0 - std::pow ((i - 0.5 * (size - 1.0)) | |||
/ ( 0.5 * (size - 1.0)), 2.0))) | |||
* factor); | |||
} | |||
break; | |||
default: | |||
jassertfalse; | |||
break; | |||
} | |||
// DC frequency amplitude must be one | |||
if (normalize) | |||
{ | |||
FloatType sum = {}; | |||
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 "FlatTop"; | |||
case kaiser: return "Kaiser"; | |||
default: jassertfalse; return ""; | |||
} | |||
} | |||
template struct WindowingFunction<float>; | |||
template struct WindowingFunction<double>; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,80 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
*/ | |||
template <typename FloatType> | |||
struct WindowingFunction | |||
{ | |||
enum WindowingMethod | |||
{ | |||
rectangular = 0, | |||
triangular, | |||
hann, | |||
hamming, | |||
blackman, | |||
blackmanHarris, | |||
flatTop, | |||
kaiser, | |||
numWindowingMethods | |||
}; | |||
//============================================================================== | |||
WindowingFunction (size_t size, WindowingMethod, | |||
bool normalize = true, FloatType beta = 0); | |||
//============================================================================== | |||
/** Fills the content of an array with a given windowing method table */ | |||
void fillWindowingTables (size_t size, WindowingMethod type, | |||
bool normalize = true, FloatType beta = 0) noexcept; | |||
/** Fills the content of an array with a given windowing method table */ | |||
static void fillWindowingTables (FloatType* samples, size_t size, WindowingMethod, | |||
bool normalize = true, FloatType beta = 0) noexcept; | |||
/** Multiply 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 |
@@ -0,0 +1,84 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
#include "processors/juce_FIRFilter.cpp" | |||
#include "processors/juce_IIRFilter.cpp" | |||
#include "processors/juce_Oversampling.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" | |||
#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" | |||
#if JUCE_USE_SIMD | |||
#include "containers/juce_SIMDRegister_test.cpp" | |||
#endif | |||
#include "frequency/juce_FFT_test.cpp" | |||
#include "processors/juce_FIRFilter_test.cpp" | |||
#endif |
@@ -0,0 +1,265 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_dsp | |||
vendor: juce | |||
version: 5.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_core, juce_audio_basics, 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 <atomic> | |||
#include <complex> | |||
#include <cmath> | |||
#include <array> | |||
//============================================================================== | |||
/** 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. | |||
The folder containing the mkl_dfti.h header must be in your header | |||
search paths when using this flag. You also need to add all the necessary | |||
intel mkl libraries to the "External Libraries to Link" field in the | |||
Projucer. | |||
*/ | |||
#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 exmaple, 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 | |||
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_Polynomial.h" | |||
#include "maths/juce_FastMathApproximations.h" | |||
#include "maths/juce_LookupTable.h" | |||
#include "containers/juce_AudioBlock.h" | |||
#include "processors/juce_ProcessContext.h" | |||
#include "processors/juce_ProcessorWrapper.h" | |||
#include "processors/juce_ProcessorDuplicator.h" | |||
#include "processors/juce_Bias.h" | |||
#include "processors/juce_Gain.h" | |||
#include "processors/juce_WaveShaper.h" | |||
#include "processors/juce_IIRFilter.h" | |||
#include "processors/juce_FIRFilter.h" | |||
#include "processors/juce_Oscillator.h" | |||
#include "processors/juce_StateVariableFilter.h" | |||
#include "processors/juce_Oversampling.h" | |||
#include "frequency/juce_FFT.h" | |||
#include "frequency/juce_Convolution.h" | |||
#include "frequency/juce_Windowing.h" | |||
#include "filter_design/juce_FilterDesign.h" | |||
// this file does not build under C++11 | |||
// #include "processors/juce_ProcessorChain.h" |
@@ -0,0 +1,27 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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" |
@@ -0,0 +1,263 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
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 |
@@ -0,0 +1,157 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 |
@@ -0,0 +1,328 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
*/ | |||
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 >= 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 | |||
*/ | |||
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() | |||
{} | |||
//============================================================================== | |||
/** 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 |
@@ -0,0 +1,318 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 (other.getNumRows() == p && n == m); | |||
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 |
@@ -0,0 +1,253 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
*/ | |||
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(), sizeof (ElementType) * (size_t) data.size()); } | |||
//============================================================================== | |||
/** 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 inline 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 euqations 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 |
@@ -0,0 +1,172 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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") {} | |||
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 |
@@ -0,0 +1,169 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
*/ | |||
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; | |||
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS || defined(DOXYGEN) | |||
/** 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 TypeToCreateFrom> | |||
Polynomial (const std::initializer_list<TypeToCreateFrom>& items) : coeffs (items) | |||
{ | |||
jassert (! coeffs.isEmpty()); | |||
} | |||
#endif | |||
//============================================================================== | |||
/** 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 = {}; | |||
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 |
@@ -0,0 +1,142 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 = double_Pi * 0.5; | |||
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 = double_Pi * 0.5; | |||
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; | |||
} | |||
std::complex<double> last = std::cos (0.5 * u * double_Pi); | |||
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; | |||
} | |||
std::complex<double> last = std::sin (0.5 * u * double_Pi); | |||
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 / double_Pi * std::asin (last); | |||
} | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,66 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
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 |
@@ -0,0 +1,59 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 }; | |||
} | |||
} |
@@ -0,0 +1,567 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
#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. */ | |||
template <> | |||
struct SIMDNativeOps<float> | |||
{ | |||
typedef __m256 vSIMDType; | |||
//============================================================================== | |||
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 *reinterpret_cast<const __m256*> (a); } | |||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE vconst (const int32_t* a) noexcept { return *reinterpret_cast<const __m256*> (a); } | |||
static forcedinline __m256 JUCE_VECTOR_CALLTYPE expand (float s) noexcept { return _mm256_broadcast_ss (&s); } | |||
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 __m256 JUCE_VECTOR_CALLTYPE multiplyAdd (__m256 a, __m256 b, __m256 c) noexcept { return _mm256_fmadd_ps (b, c, a); } | |||
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 __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); | |||
return ((float*) &retval)[0]; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Double-precision floating point AVX intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<double> | |||
{ | |||
typedef __m256d vSIMDType; | |||
//============================================================================== | |||
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 *reinterpret_cast<const __m256d*> (a); } | |||
static forcedinline __m256d JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return *reinterpret_cast<const __m256d*> (a); } | |||
static forcedinline __m256d expand (double s) noexcept { return _mm256_broadcast_sd (&s); } | |||
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 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 __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 __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); | |||
return ((double*) &retval)[0]; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Signed 8-bit integer AVX intrinsics */ | |||
template <> | |||
struct SIMDNativeOps<int8_t> | |||
{ | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (int8_t, kAllBitsSet); | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const int8_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int8_t s) noexcept { return _mm256_set1_epi8 (s); } | |||
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, vconst (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 __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 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); | |||
} | |||
const int8_t* lo_ptr = reinterpret_cast<const int8_t*> (&lo); | |||
const int8_t* hi_ptr = reinterpret_cast<const int8_t*> (&hi); | |||
return (int8_t) (lo_ptr[0] + hi_ptr[0] + lo_ptr[16] + hi_ptr[16]); | |||
} | |||
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. */ | |||
template <> | |||
struct SIMDNativeOps<uint8_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (uint8_t, kHighBit); | |||
DECLARE_AVX_SIMD_CONST (uint8_t, kAllBitsSet); | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const uint8_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, vconst (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 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, vconst (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 __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 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); | |||
} | |||
const uint8_t* lo_ptr = reinterpret_cast<const uint8_t*> (&lo); | |||
const uint8_t* hi_ptr = reinterpret_cast<const uint8_t*> (&hi); | |||
return (uint8_t) (lo_ptr[0] + hi_ptr[0] + lo_ptr[16] + hi_ptr[16]); | |||
} | |||
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. */ | |||
template <> | |||
struct SIMDNativeOps<int16_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (int16_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const int16_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int16_t s) noexcept { return _mm256_set1_epi16 (s); } | |||
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, vconst (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 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); | |||
int16_t* ptr = reinterpret_cast<int16_t*> (&tmp); | |||
return (int16_t) (ptr[0] + ptr[8]); | |||
} | |||
}; | |||
//============================================================================== | |||
/** Unsigned 16-bit integer AVX intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint16_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (uint16_t, kHighBit); | |||
DECLARE_AVX_SIMD_CONST (uint16_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const uint16_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, vconst (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 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, vconst (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 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); | |||
uint16_t* ptr = reinterpret_cast<uint16_t*> (&tmp); | |||
return (uint16_t) (ptr[0] + ptr[8]); | |||
} | |||
}; | |||
//============================================================================== | |||
/** Signed 32-bit integer AVX intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int32_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (int32_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const int32_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE expand (int32_t s) noexcept { return _mm256_set1_epi32 (s); } | |||
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, vconst (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 int32_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept | |||
{ | |||
__m256i tmp = _mm256_hadd_epi32 (a, a); | |||
tmp = _mm256_hadd_epi32 (tmp, tmp); | |||
int32_t* ptr = reinterpret_cast<int32_t*> (&tmp); | |||
return ptr[0] + ptr[4]; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Unsigned 32-bit integer AVX intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint32_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (uint32_t, kAllBitsSet); | |||
DECLARE_AVX_SIMD_CONST (uint32_t, kHighBit); | |||
//============================================================================== | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const uint32_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, vconst (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 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, vconst (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 uint32_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept | |||
{ | |||
__m256i tmp = _mm256_hadd_epi32 (a, a); | |||
tmp = _mm256_hadd_epi32 (tmp, tmp); | |||
uint32_t* ptr = reinterpret_cast<uint32_t*> (&tmp); | |||
return ptr[0] + ptr[4]; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Signed 64-bit integer AVX intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int64_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (int64_t, kAllBitsSet); | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
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, vconst (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 __m256i JUCE_VECTOR_CALLTYPE expand (int64_t s) noexcept | |||
{ | |||
#ifdef _MSC_VER | |||
__m256d tmp = _mm256_broadcast_sd (reinterpret_cast<const double*> (&s)); | |||
return *reinterpret_cast<const __m256i*> (&tmp); | |||
#else | |||
return _mm256_set1_epi64x ((int64_t) s); | |||
#endif | |||
} | |||
static forcedinline int64_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept | |||
{ | |||
const int64_t* ptr = reinterpret_cast<const int64_t*> (&a); | |||
return ptr[0] + ptr[1] + ptr[2] + ptr[3]; | |||
} | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept | |||
{ | |||
__m256i retval; | |||
const int64_t* aptr = reinterpret_cast<const int64_t*> (&a); | |||
const int64_t* bptr = reinterpret_cast<const int64_t*> (&b); | |||
int64_t* dst = reinterpret_cast<int64_t*> (&retval); | |||
for (int i = 0; i < 4; ++i) | |||
dst[i] = aptr[i] * bptr[i]; | |||
return retval; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Unsigned 64-bit integer AVX intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint64_t> | |||
{ | |||
//============================================================================== | |||
typedef __m256i vSIMDType; | |||
//============================================================================== | |||
DECLARE_AVX_SIMD_CONST (uint64_t, kAllBitsSet); | |||
DECLARE_AVX_SIMD_CONST (uint64_t, kHighBit); | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE vconst (const uint64_t* a) noexcept { return *reinterpret_cast<const __m256i*> (a); } | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE ssign (__m256i a) noexcept { return _mm256_xor_si256 (a, vconst (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, vconst (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 __m256i JUCE_VECTOR_CALLTYPE expand (uint64_t s) noexcept | |||
{ | |||
#ifdef _MSC_VER | |||
__m256d tmp = _mm256_broadcast_sd (reinterpret_cast<const double*> (&s)); | |||
return *reinterpret_cast<const __m256i*> (&tmp); | |||
#else | |||
return _mm256_set1_epi64x ((int64_t) s); | |||
#endif | |||
} | |||
static forcedinline uint64_t JUCE_VECTOR_CALLTYPE sum (__m256i a) noexcept | |||
{ | |||
const uint64_t* ptr = reinterpret_cast<const uint64_t*> (&a); | |||
return ptr[0] + ptr[1] + ptr[2] + ptr[3]; | |||
} | |||
static forcedinline __m256i JUCE_VECTOR_CALLTYPE mul (__m256i a, __m256i b) noexcept | |||
{ | |||
__m256i retval; | |||
const uint64_t* aptr = reinterpret_cast<const uint64_t*> (&a); | |||
const uint64_t* bptr = reinterpret_cast<const uint64_t*> (&b); | |||
uint64_t* dst = reinterpret_cast<uint64_t*> (&retval); | |||
for (int i = 0; i < 4; ++i) | |||
dst[i] = aptr[i] * bptr[i]; | |||
return retval; | |||
} | |||
}; | |||
#endif | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,220 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 { typedef Primitive type; }; | |||
template <> struct MaskTypeFor <float> { typedef uint32_t type; }; | |||
template <> struct MaskTypeFor <double> { typedef uint64_t type; }; | |||
template <> struct MaskTypeFor <char> { typedef uint8_t type; }; | |||
template <> struct MaskTypeFor <int8_t> { typedef uint8_t type; }; | |||
template <> struct MaskTypeFor <int16_t> { typedef uint16_t type; }; | |||
template <> struct MaskTypeFor <int32_t> { typedef uint32_t type; }; | |||
template <> struct MaskTypeFor <int64_t> { typedef uint64_t type; }; | |||
template <> struct MaskTypeFor <std::complex<float> > { typedef uint32_t type; }; | |||
template <> struct MaskTypeFor <std::complex<double> > { typedef uint64_t type; }; | |||
template <typename Primitive> struct PrimitiveType { typedef Primitive type; }; | |||
template <typename Primitive> struct PrimitiveType<std::complex<Primitive> > { typedef 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. | |||
*/ | |||
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<n>::value; | |||
// corresponding mask type | |||
typedef typename SIMDInternal::MaskTypeFor<ScalarType>::type MaskType; | |||
// 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 vSIMDType bit_not (vSIMDType a) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<MaskType*> (&retval); | |||
auto* aSrc = reinterpret_cast<const MaskType*> (&a); | |||
for (size_t i = 0; i < n; ++i) | |||
dst [i] = ~aSrc [i]; | |||
return retval; | |||
} | |||
static forcedinline ScalarType sum (vSIMDType a) noexcept | |||
{ | |||
auto retval = static_cast<ScalarType> (0); | |||
auto* aSrc = reinterpret_cast<const ScalarType*> (&a); | |||
for (size_t i = 0; i < n; ++i) | |||
retval += aSrc [i]; | |||
return retval; | |||
} | |||
static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<ScalarType*> (&retval); | |||
auto* aSrc = reinterpret_cast<const ScalarType*> (&a); | |||
auto* bSrc = reinterpret_cast<const ScalarType*> (&b); | |||
auto* cSrc = reinterpret_cast<const ScalarType*> (&c); | |||
for (size_t i = 0; i < n; ++i) | |||
dst [i] = aSrc [i] + (bSrc [i] * cSrc [i]); | |||
return retval; | |||
} | |||
//============================================================================== | |||
static forcedinline vSIMDType cmplxmul (vSIMDType a, vSIMDType b) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<std::complex<ScalarType>*> (&retval); | |||
auto* aSrc = reinterpret_cast<const std::complex<ScalarType>*> (&a); | |||
auto* bSrc = reinterpret_cast<const std::complex<ScalarType>*> (&b); | |||
const int m = n >> 1; | |||
for (int i = 0; i < m; ++i) | |||
dst [i] = aSrc [i] * bSrc [i]; | |||
return retval; | |||
} | |||
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 a, vSIMDType b) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<ScalarType*> (&retval); | |||
auto* aSrc = reinterpret_cast<const ScalarType*> (&a); | |||
auto* bSrc = reinterpret_cast<const ScalarType*> (&b); | |||
for (size_t i = 0; i < n; ++i) | |||
dst [i] = Op::op (aSrc [i], bSrc [i]); | |||
return retval; | |||
} | |||
template <typename Op> | |||
static forcedinline vSIMDType cmp (vSIMDType a, vSIMDType b) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<MaskType*> (&retval); | |||
auto* aSrc = reinterpret_cast<const ScalarType*> (&a); | |||
auto* bSrc = reinterpret_cast<const ScalarType*> (&b); | |||
for (size_t i = 0; i < n; ++i) | |||
dst [i] = Op::op (aSrc [i], bSrc [i]) ? static_cast<MaskType> (-1) : static_cast<MaskType> (0); | |||
return retval; | |||
} | |||
template <typename Op> | |||
static forcedinline vSIMDType bitapply (vSIMDType a, vSIMDType b) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<MaskType*> (&retval); | |||
auto* aSrc = reinterpret_cast<const MaskType*> (&a); | |||
auto* bSrc = reinterpret_cast<const MaskType*> (&b); | |||
for (size_t i = 0; i < n; ++i) | |||
dst [i] = Op::op (aSrc [i], bSrc [i]); | |||
return retval; | |||
} | |||
static forcedinline vSIMDType expand (ScalarType s) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<ScalarType*> (&retval); | |||
for (size_t i = 0; i < n; ++i) | |||
dst [i] = s; | |||
return retval; | |||
} | |||
template <unsigned int shuffle_idx> | |||
static forcedinline vSIMDType shuffle (vSIMDType a) noexcept | |||
{ | |||
vSIMDType retval; | |||
auto* dst = reinterpret_cast<ScalarType*> (&retval); | |||
auto* aSrc = reinterpret_cast<const ScalarType*> (&a); | |||
// 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) | |||
dst [i] = aSrc [(shuffle_idx >> (bits * i)) & mask]; | |||
return retval; | |||
} | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,44 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 }; | |||
} | |||
} |
@@ -0,0 +1,394 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
#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; | |||
//============================================================================== | |||
/** Single-precision floating point NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<float> | |||
{ | |||
//============================================================================== | |||
typedef float32x4_t vSIMDType; | |||
typedef uint32x4_t vMaskType; | |||
typedef SIMDFallbackOps<float, vSIMDType> fb; | |||
//============================================================================== | |||
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 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 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 float sum (vSIMDType a) noexcept { return fb::sum (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 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))); | |||
} | |||
}; | |||
//============================================================================== | |||
/** Double-precision floating point NEON intrinsics does not exist in NEON | |||
so we need to emulate this. | |||
*/ | |||
template <> | |||
struct SIMDNativeOps<double> | |||
{ | |||
//============================================================================== | |||
typedef struct { double values [2]; } vSIMDType; | |||
typedef SIMDFallbackOps<double, vSIMDType> fb; | |||
static forcedinline vSIMDType expand (double s) noexcept { return fb::expand (s); } | |||
static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return fb::add (a, b); } | |||
static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return fb::sub (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 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 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; } | |||
}; | |||
//============================================================================== | |||
/** Signed 8-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int8_t> | |||
{ | |||
//============================================================================== | |||
typedef int8x16_t vSIMDType; | |||
typedef SIMDFallbackOps<int8_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (int8_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (int8_t s) noexcept { return vdupq_n_s8 (s); } | |||
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 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); } | |||
}; | |||
//============================================================================== | |||
/** Unsigned 8-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint8_t> | |||
{ | |||
//============================================================================== | |||
typedef uint8x16_t vSIMDType; | |||
typedef SIMDFallbackOps<uint8_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (uint8_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (uint8_t s) noexcept { return vdupq_n_u8 (s); } | |||
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 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); } | |||
}; | |||
//============================================================================== | |||
/** Signed 16-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int16_t> | |||
{ | |||
//============================================================================== | |||
typedef int16x8_t vSIMDType; | |||
typedef SIMDFallbackOps<int16_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (int16_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (int16_t s) noexcept { return vdupq_n_s16 (s); } | |||
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 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); } | |||
}; | |||
//============================================================================== | |||
/** Unsigned 16-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint16_t> | |||
{ | |||
//============================================================================== | |||
typedef uint16x8_t vSIMDType; | |||
typedef SIMDFallbackOps<uint16_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (uint16_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (uint16_t s) noexcept { return vdupq_n_u16 (s); } | |||
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 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); } | |||
}; | |||
//============================================================================== | |||
/** Signed 32-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int32_t> | |||
{ | |||
//============================================================================== | |||
typedef int32x4_t vSIMDType; | |||
typedef SIMDFallbackOps<int32_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (int32_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (int32_t s) noexcept { return vdupq_n_s32 (s); } | |||
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 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 int32_t sum (vSIMDType a) noexcept { return fb::sum (a); } | |||
}; | |||
//============================================================================== | |||
/** Unsigned 32-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint32_t> | |||
{ | |||
//============================================================================== | |||
typedef uint32x4_t vSIMDType; | |||
typedef SIMDFallbackOps<uint32_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (uint32_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (uint32_t s) noexcept { return vdupq_n_u32 (s); } | |||
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 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 uint32_t sum (vSIMDType a) noexcept { return fb::sum (a); } | |||
}; | |||
//============================================================================== | |||
/** Signed 64-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int64_t> | |||
{ | |||
//============================================================================== | |||
typedef int64x2_t vSIMDType; | |||
typedef SIMDFallbackOps<int64_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (int64_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (int64_t s) noexcept { return vdupq_n_s64 (s); } | |||
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 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); } | |||
}; | |||
//============================================================================== | |||
/** Unsigned 64-bit integer NEON intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint64_t> | |||
{ | |||
//============================================================================== | |||
typedef uint64x2_t vSIMDType; | |||
typedef SIMDFallbackOps<uint64_t, vSIMDType> fb; | |||
//============================================================================== | |||
DECLARE_NEON_SIMD_CONST (uint64_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline vSIMDType expand (uint64_t s) noexcept { return vdupq_n_u64 (s); } | |||
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 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); } | |||
}; | |||
#endif | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,59 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 }; | |||
} | |||
} |
@@ -0,0 +1,748 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
#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. */ | |||
template <> | |||
struct SIMDNativeOps<float> | |||
{ | |||
//============================================================================== | |||
typedef __m128 vSIMDType; | |||
//============================================================================== | |||
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 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 __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 __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 ((float*) &retval) [0]; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Double-precision floating point SSE intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<double> | |||
{ | |||
//============================================================================== | |||
typedef __m128d vSIMDType; | |||
//============================================================================== | |||
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 *reinterpret_cast<const __m128d*> (a); } | |||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return *reinterpret_cast<const __m128d*> (a); } | |||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE expand (double s) noexcept { return _mm_load1_pd (&s); } | |||
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 __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 oddevensum (__m128d a) noexcept { return 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 ((double*) &retval) [0]; | |||
} | |||
}; | |||
//============================================================================== | |||
/** Signed 8-bit integer SSE intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int8_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
DECLARE_SSE_SIMD_CONST (int8_t, kAllBitsSet); | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int8_t* a) noexcept { return *reinterpret_cast<const __m128i*> (a); } | |||
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 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); | |||
} | |||
const int8_t* lo_ptr = reinterpret_cast<const int8_t*> (&lo); | |||
const int8_t* hi_ptr = reinterpret_cast<const int8_t*> (&hi); | |||
return lo_ptr[0] + hi_ptr[0]; | |||
#else | |||
int8_t sum = 0; | |||
const int8_t* src = reinterpret_cast<const int8_t*> (&a); | |||
for (std::size_t i = 0; i < (sizeof (vSIMDType) / sizeof(int8_t)); ++i) | |||
sum += src [i]; | |||
return sum; | |||
#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. */ | |||
template <> | |||
struct SIMDNativeOps<uint8_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
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 *reinterpret_cast<const __m128i*> (a); } | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); } | |||
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 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); | |||
} | |||
const uint8_t* lo_ptr = reinterpret_cast<const uint8_t*> (&lo); | |||
const uint8_t* hi_ptr = reinterpret_cast<const uint8_t*> (&hi); | |||
return lo_ptr[0] + hi_ptr[0]; | |||
#else | |||
uint8_t sum = 0; | |||
const uint8_t* src = reinterpret_cast<const uint8_t*> (&a); | |||
for (std::size_t i = 0; i < (sizeof (vSIMDType) / sizeof(int8_t)); ++i) | |||
sum += src [i]; | |||
return sum; | |||
#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. */ | |||
template <> | |||
struct SIMDNativeOps<int16_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
DECLARE_SSE_SIMD_CONST (int16_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int16_t* a) noexcept { return *reinterpret_cast<const __m128i*> (a); } | |||
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 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 *reinterpret_cast<int16_t*> (&tmp); | |||
#else | |||
int16_t sum = 0; | |||
const int16_t* src = reinterpret_cast<const int16_t*> (&a); | |||
for (std::size_t i = 0; i < (sizeof (vSIMDType) / sizeof(int16_t)); ++i) | |||
sum += src [i]; | |||
return sum; | |||
#endif | |||
} | |||
}; | |||
//============================================================================== | |||
/** Unsigned 16-bit integer SSE intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint16_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
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 *reinterpret_cast<const __m128i*> (a); } | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); } | |||
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 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 *reinterpret_cast<uint16_t*> (&tmp); | |||
#else | |||
uint16_t sum = 0; | |||
const uint16_t* src = reinterpret_cast<const uint16_t*> (&a); | |||
for (std::size_t i = 0; i < (sizeof (vSIMDType) / sizeof(uint16_t)); ++i) | |||
sum += src [i]; | |||
return sum; | |||
#endif | |||
} | |||
}; | |||
//============================================================================== | |||
/** Signed 32-bit integer SSE intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<int32_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
DECLARE_SSE_SIMD_CONST (int32_t, kAllBitsSet); | |||
//============================================================================== | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int32_t* a) noexcept { return *reinterpret_cast<const __m128i*> (a); } | |||
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 int32_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept | |||
{ | |||
#ifdef __SSSE3__ | |||
__m128i tmp = _mm_hadd_epi32 (a, a); | |||
tmp = _mm_hadd_epi32 (tmp, tmp); | |||
return *reinterpret_cast<int32_t*> (&tmp); | |||
#else | |||
int32_t sum = 0; | |||
const int32_t* src = reinterpret_cast<const int32_t*> (&a); | |||
for (std::size_t i = 0; i < (sizeof (vSIMDType) / sizeof(int32_t)); ++i) | |||
sum += src [i]; | |||
return sum; | |||
#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. */ | |||
template <> | |||
struct SIMDNativeOps<uint32_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
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 *reinterpret_cast<const __m128i*> (a); } | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); } | |||
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 uint32_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept | |||
{ | |||
#ifdef __SSSE3__ | |||
__m128i tmp = _mm_hadd_epi32 (a, a); | |||
tmp = _mm_hadd_epi32 (tmp, tmp); | |||
return *reinterpret_cast<uint32_t*> (&tmp); | |||
#else | |||
uint32_t sum = 0; | |||
const uint32_t* src = reinterpret_cast<const uint32_t*> (&a); | |||
for (std::size_t i = 0; i < (sizeof (vSIMDType) / sizeof(uint32_t)); ++i) | |||
sum += src [i]; | |||
return sum; | |||
#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. */ | |||
template <> | |||
struct SIMDNativeOps<int64_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
DECLARE_SSE_SIMD_CONST (int64_t, kAllBitsSet); | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (int64_t s) noexcept | |||
{ | |||
__m128i retval; | |||
int64_t* ptr = reinterpret_cast<int64_t*> (&retval); | |||
ptr[0] = ptr[1] = s; | |||
return retval; | |||
} | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const int64_t* a) noexcept { return *reinterpret_cast<const __m128i*> (a); } | |||
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 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 int64_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept | |||
{ | |||
const int64_t* ptr = reinterpret_cast<const int64_t*> (&a); | |||
return ptr[0] + ptr[1]; | |||
} | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept | |||
{ | |||
__m128i retval; | |||
const int64_t* aptr = reinterpret_cast<const int64_t*> (&a); | |||
const int64_t* bptr = reinterpret_cast<const int64_t*> (&b); | |||
int64_t* dst = reinterpret_cast<int64_t*> (&retval); | |||
dst[0] = aptr[0] * bptr[0]; | |||
dst[1] = aptr[1] * bptr[1]; | |||
return retval; | |||
} | |||
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_1__) && !defined(__clang__) | |||
return _mm_cmpgt_epi64 (a, b); | |||
#else | |||
__m128i retval; | |||
const int64_t* aptr = reinterpret_cast<const int64_t*> (&a); | |||
const int64_t* bptr = reinterpret_cast<const int64_t*> (&b); | |||
int64_t* dst = reinterpret_cast<int64_t*> (&retval); | |||
dst[0] = aptr[0] > bptr[0] ? -1LL : 0; | |||
dst[1] = aptr[1] > bptr[1] ? -1LL : 0; | |||
return retval; | |||
#endif | |||
} | |||
}; | |||
//============================================================================== | |||
/** Unsigned 64-bit integer SSE intrinsics. */ | |||
template <> | |||
struct SIMDNativeOps<uint64_t> | |||
{ | |||
//============================================================================== | |||
typedef __m128i vSIMDType; | |||
//============================================================================== | |||
DECLARE_SSE_SIMD_CONST (uint64_t, kAllBitsSet); | |||
DECLARE_SSE_SIMD_CONST (uint64_t, kHighBit); | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE expand (uint64_t s) noexcept | |||
{ | |||
__m128i retval; | |||
uint64_t* ptr = reinterpret_cast<uint64_t*> (&retval); | |||
ptr[0] = ptr[1] = s; | |||
return retval; | |||
} | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE vconst (const uint64_t* a) noexcept { return *reinterpret_cast<const __m128i*> (a); } | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE ssign (__m128i a) noexcept { return _mm_xor_si128 (a, vconst (kHighBit)); } | |||
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 uint64_t JUCE_VECTOR_CALLTYPE sum (__m128i a) noexcept | |||
{ | |||
const uint64_t* ptr = reinterpret_cast<const uint64_t*> (&a); | |||
return ptr[0] + ptr[1]; | |||
} | |||
static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept | |||
{ | |||
__m128i retval; | |||
const uint64_t* aptr = reinterpret_cast<const uint64_t*> (&a); | |||
const uint64_t* bptr = reinterpret_cast<const uint64_t*> (&b); | |||
uint64_t* dst = reinterpret_cast<uint64_t*> (&retval); | |||
dst[0] = aptr[0] * bptr[0]; | |||
dst[1] = aptr[1] * bptr[1]; | |||
return retval; | |||
} | |||
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_1__) && !defined(__clang__) | |||
return _mm_cmpgt_epi64 (a, b); | |||
#else | |||
__m128i retval; | |||
const uint64_t* aptr = reinterpret_cast<const uint64_t*> (&a); | |||
const uint64_t* bptr = reinterpret_cast<const uint64_t*> (&b); | |||
uint64_t* dst = reinterpret_cast<uint64_t*> (&retval); | |||
dst[0] = aptr[0] > bptr[0] ? (uint64_t) -1LL : 0; | |||
dst[1] = aptr[1] > bptr[1] ? (uint64_t) -1LL : 0; | |||
return retval; | |||
#endif | |||
} | |||
}; | |||
#endif | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,146 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 ;). | |||
*/ | |||
template <typename FloatType> | |||
class Bias | |||
{ | |||
public: | |||
Bias() noexcept {} | |||
//============================================================================== | |||
/** 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.setValue(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) const 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 (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 | |||
{ | |||
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)); | |||
} | |||
} | |||
private: | |||
//============================================================================== | |||
LinearSmoothedValue<FloatType> bias; | |||
double sampleRate = 0, rampDurationSeconds = 0; | |||
void updateRamp() noexcept | |||
{ | |||
if (sampleRate > 0) | |||
bias.reset (sampleRate, rampDurationSeconds); | |||
} | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,162 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 (-2.0 * double_Pi * 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 (-2.0 * double_Pi * 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 (-2.0 * double_Pi * 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 (-2.0 * double_Pi * 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 |
@@ -0,0 +1,283 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
*/ | |||
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; | |||
//============================================================================== | |||
/** 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 (Coefficients<NumericType>* coefficientsToUse) : coefficients (coefficientsToUse) { reset(); } | |||
/** Creates a copy of another filter. */ | |||
Filter (const Filter&) = default; | |||
/** Creates a copy of another filter. */ | |||
Filter (Filter&&) = default; | |||
/** Assignment operator */ | |||
Filter& operator= (const Filter&) = default; | |||
/** Assignment operator */ | |||
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. | |||
jassert (spec.numChannels == 1); | |||
ignoreUnused (spec); | |||
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 called 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 as 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; | |||
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 = {}; | |||
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 | |||
*/ | |||
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 copy of another filter. */ | |||
Coefficients (const Coefficients&) = default; | |||
/** Move constructor. */ | |||
Coefficients (Coefficients&&) = default; | |||
/** Creates a set of coefficients from an array of samples. */ | |||
Coefficients (const NumericType* samples, size_t numSamples) : coefficients (samples, (int) numSamples) {} | |||
/** Creates a copy of another filter. */ | |||
Coefficients& operator= (const Coefficients& other) { coefficients = other.coefficients; return *this; } | |||
/** Creates a copy of another filter. */ | |||
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 |
@@ -0,0 +1,210 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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; | |||
} | |||
}; | |||
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()); | |||
} | |||
}; | |||
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 | |||
+ (SIMDRegister<NumericType>::SIMDRegisterSize / sizeof (SampleType))); | |||
SampleType* buffer = reinterpret_cast<SampleType*> (SIMDRegister<NumericType>::getNextSIMDAlignedPtr (reinterpret_cast<NumericType*> (scratchBuffer.getData()))); | |||
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{}; | |||
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<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<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>(); | |||
runTestForType<TheTest, SIMDRegister<float>, float> (); | |||
runTestForType<TheTest, SIMDRegister<double>, double>(); | |||
} | |||
public: | |||
FIRFilterTest() : UnitTest ("FIR Filter") {} | |||
void runTest() override | |||
{ | |||
runTestForAllTypes<LargeBlockTest> ("Large Blocks"); | |||
runTestForAllTypes<SampleBySampleTest> ("Sample by Sample"); | |||
runTestForAllTypes<SplitBlockTest> ("Split Block"); | |||
} | |||
}; | |||
static FIRFilterTest firFilterUnitTest; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,135 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
template <typename FloatType> | |||
class Gain | |||
{ | |||
public: | |||
Gain() noexcept {} | |||
//============================================================================== | |||
/** Applies a new gain as a linear value. */ | |||
void setGainLinear (FloatType newGain) noexcept { gain.setValue (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 (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 | |||
{ | |||
auto* gains = static_cast<FloatType*> (alloca (sizeof (FloatType) * len)); | |||
for (size_t i = 0; i < len; ++i) | |||
gains[i] = gain.getNextValue(); | |||
for (size_t chan = 0; chan < numChannels; ++chan) | |||
FloatVectorOperations::multiply (outBlock.getChannelPointer (chan), | |||
inBlock.getChannelPointer (chan), | |||
gains, static_cast<int> (len)); | |||
} | |||
} | |||
private: | |||
//============================================================================== | |||
LinearSmoothedValue<FloatType> gain; | |||
double sampleRate = 0, rampDurationSeconds = 0; | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,494 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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> | |||
IIR::Coefficients<NumericType>::Coefficients() | |||
: coefficients ({ NumericType(), | |||
NumericType(), | |||
NumericType(), | |||
NumericType(), | |||
NumericType() }) | |||
{ | |||
} | |||
template <typename NumericType> | |||
IIR::Coefficients<NumericType>::Coefficients (const Coefficients& other) | |||
: coefficients (other.coefficients) | |||
{ | |||
} | |||
template <typename NumericType> | |||
IIR::Coefficients<NumericType>& IIR::Coefficients<NumericType>::operator= (const Coefficients& other) | |||
{ | |||
coefficients = other.coefficients; | |||
return *this; | |||
} | |||
template <typename NumericType> | |||
IIR::Coefficients<NumericType>::Coefficients (NumericType b0, NumericType b1, | |||
NumericType a0, NumericType a1) | |||
{ | |||
jassert (a0 != 0); | |||
coefficients.clear(); | |||
auto a0inv = static_cast<NumericType> (1) / a0; | |||
coefficients.add (b0 * a0inv, | |||
b1 * a0inv, | |||
a1 * a0inv); | |||
} | |||
template <typename NumericType> | |||
IIR::Coefficients<NumericType>::Coefficients (NumericType b0, NumericType b1, NumericType b2, | |||
NumericType a0, NumericType a1, NumericType a2) | |||
{ | |||
jassert (a0 != 0); | |||
coefficients.clear(); | |||
auto a0inv = static_cast<NumericType> (1) / a0; | |||
coefficients.add (b0 * a0inv, | |||
b1 * a0inv, | |||
b2 * a0inv, | |||
a1 * a0inv, | |||
a2 * a0inv); | |||
} | |||
template <typename NumericType> | |||
IIR::Coefficients<NumericType>::Coefficients (NumericType b0, NumericType b1, NumericType b2, NumericType b3, | |||
NumericType a0, NumericType a1, NumericType a2, NumericType a3) | |||
{ | |||
jassert (a0 != 0); | |||
coefficients.clear(); | |||
auto a0inv = static_cast<NumericType> (1) / a0; | |||
coefficients.add (b0 * a0inv, | |||
b1 * a0inv, | |||
b2 * a0inv, | |||
b3 * a0inv, | |||
a1 * a0inv, | |||
a2 * a0inv, | |||
a3 * a0inv); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (n, n, n + 1, n - 1); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (1, -1, n + 1, n - 1); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (n - 1, n + 1, n + 1, n - 1); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<NumericType>::makeLowPass (double sampleRate, | |||
NumericType frequency) | |||
{ | |||
return makeLowPass (sampleRate, frequency, inverseRootTwo); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (c1, c1 * 2, c1, | |||
1, c1 * 2 * (1 - nSquared), | |||
c1 * (1 - invQ * n + nSquared)); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<NumericType>::makeHighPass (double sampleRate, | |||
NumericType frequency) | |||
{ | |||
return makeHighPass (sampleRate, frequency, inverseRootTwo); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (c1, c1 * -2, c1, | |||
1, c1 * 2 * (nSquared - 1), | |||
c1 * (1 - invQ * n + nSquared)); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<NumericType>::makeBandPass (double sampleRate, | |||
NumericType frequency) | |||
{ | |||
return makeBandPass (sampleRate, frequency, inverseRootTwo); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (c1 * n * invQ, 0, | |||
-c1 * n * invQ, 1, | |||
c1 * 2 * (1 - nSquared), | |||
c1 * (1 - invQ * n + nSquared)); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<NumericType>::makeNotch (double sampleRate, | |||
NumericType frequency) | |||
{ | |||
return makeNotch (sampleRate, frequency, inverseRootTwo); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared)); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<NumericType>::makeAllPass (double sampleRate, | |||
NumericType frequency) | |||
{ | |||
return makeAllPass (sampleRate, frequency, inverseRootTwo); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (b0, b1, 1, 1, b1, b0); | |||
} | |||
template <typename NumericType> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (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> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (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> | |||
typename IIR::Coefficients<NumericType>::Ptr IIR::Coefficients<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 new Coefficients (1 + alphaTimesA, c2, | |||
1 - alphaTimesA, | |||
1 + alphaOverA, c2, | |||
1 - alphaOverA); | |||
} | |||
template <typename NumericType> | |||
size_t IIR::Coefficients<NumericType>::getFilterOrder() const noexcept | |||
{ | |||
return (static_cast<size_t> (coefficients.size()) - 1) / 2; | |||
} | |||
template <typename NumericType> | |||
double IIR::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 (-2.0 * double_Pi * 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 IIR::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 (-2.0 * double_Pi * 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 IIR::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 (-2.0 * double_Pi * 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 IIR::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 (-2.0 * double_Pi * 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 IIR::Coefficients<float>; | |||
template struct IIR::Coefficients<double>; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,291 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
============================================================================== | |||
*/ | |||
/** | |||
Classes for IIR filter processing. | |||
*/ | |||
namespace juce | |||
{ | |||
namespace dsp | |||
{ | |||
namespace IIR | |||
{ | |||
template <typename NumericType> | |||
struct Coefficients; | |||
/** | |||
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 | |||
*/ | |||
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; | |||
//============================================================================== | |||
/** Creates a filter. | |||
Initially the filter is inactive, so will have no effect on samples that | |||
you process with it. Use the setCoefficients() method to turn it into the | |||
type of filter needed. | |||
*/ | |||
Filter(); | |||
/** Creates a filter with a given set of coefficients. */ | |||
Filter (Coefficients<NumericType>* coefficientsToUse); | |||
/** Creates a copy of another filter. */ | |||
Filter (const Filter&) = default; | |||
/** Move constructor. */ | |||
Filter (Filter&&) = default; | |||
//============================================================================== | |||
/** The coefficients of the IIR filter. It's up to the called 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; | |||
//============================================================================== | |||
/** 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 as a block of samples */ | |||
template <typename ProcessContext> | |||
void process (const ProcessContext& context) noexcept; | |||
/** 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(); | |||
//============================================================================== | |||
HeapBlock<SampleType> memory; | |||
SampleType* state = nullptr; | |||
size_t order = 0; | |||
JUCE_LEAK_DETECTOR (Filter) | |||
}; | |||
//============================================================================== | |||
/** A set of coefficients for use in an Filter object. | |||
@see IIR::Filter | |||
*/ | |||
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, NumericType b2, NumericType b3, | |||
NumericType a0, NumericType a1, NumericType a2, NumericType a3); | |||
/** Creates a copy of another filter. */ | |||
Coefficients (const Coefficients&); | |||
/** Creates a copy of another filter. */ | |||
Coefficients& operator= (const Coefficients&); | |||
/** 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: | |||
// Unfortunately, std::sqrt is not marked as constexpr just yet in all compilers | |||
static constexpr NumericType inverseRootTwo = static_cast<NumericType> (0.70710678118654752440L); | |||
}; | |||
} // namespace IIR | |||
} // namespace dsp | |||
} // namespace juce | |||
#include "juce_IIRFilter_Impl.h" |
@@ -0,0 +1,227 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 SampleType> | |||
Filter<SampleType>::Filter() | |||
: coefficients (new Coefficients<typename Filter<SampleType>::NumericType> (1, 0, 1, 0)) | |||
{ | |||
reset(); | |||
} | |||
template <typename SampleType> | |||
Filter<SampleType>::Filter (Coefficients<typename Filter<SampleType>::NumericType>* c) | |||
: coefficients (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> | |||
void Filter<SampleType>::process (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 in = src[i]; | |||
auto out = in * b0 + lv1; | |||
dst[i] = out; | |||
lv1 = (in * b1) - (out * 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 in = src[i]; | |||
auto out = (in * b0) + lv1; | |||
dst[i] = out; | |||
lv1 = (in * b1) - (out * a1) + lv2; | |||
lv2 = (in * b2) - (out * 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 in = src[i]; | |||
auto out = (in * b0) + lv1; | |||
dst[i] = out; | |||
lv1 = (in * b1) - (out * a1) + lv2; | |||
lv2 = (in * b2) - (out * a2) + lv3; | |||
lv3 = (in * b3) - (out * 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 in = src[i]; | |||
auto out = (in * coeffs[0]) + state[0]; | |||
dst[i] = out; | |||
for (size_t j = 0; j < order - 1; ++j) | |||
state[j] = (in * coeffs[j + 1]) - (out * coeffs[order + j + 1]) + state[j + 1]; | |||
state[order - 1] = (in * coeffs[order]) - (out * coeffs[order * 2]); | |||
} | |||
snapToZero(); | |||
} | |||
} | |||
} | |||
template <typename SampleType> | |||
SampleType JUCE_VECTOR_CALLTYPE Filter<SampleType>::processSample (SampleType sample) noexcept | |||
{ | |||
check(); | |||
auto* c = coefficients->getRawCoefficients(); | |||
auto out = (c[0] * sample) + state[0]; | |||
for (size_t j = 0; j < order - 1; ++j) | |||
state[j] = (c[j + 1] * sample) - (c[order + j + 1] * out) + state[j + 1]; | |||
state[order - 1] = (c[order] * sample) - (c[order * 2] * out); | |||
return out; | |||
} | |||
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 |
@@ -0,0 +1,162 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
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 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) | |||
: generator (function), frequency (440.0f) | |||
{ | |||
if (lookupTableNumPoints != 0) | |||
{ | |||
auto table = new LookupTableTransform<NumericType> (generator, static_cast <NumericType> (-1.0 * double_Pi), | |||
static_cast<NumericType> (double_Pi), lookupTableNumPoints); | |||
lookupTable = table; | |||
generator = [table] (NumericType x) { return (*table) (x); }; | |||
} | |||
} | |||
//============================================================================== | |||
/** Sets the frequency of the oscillator. */ | |||
void setFrequency (NumericType newGain) noexcept { frequency.setValue (newGain); } | |||
/** 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 | |||
{ | |||
pos = 0.0; | |||
if (sampleRate > 0) | |||
frequency.reset (sampleRate, 0.05); | |||
} | |||
//============================================================================== | |||
/** Returns the result of processing a single sample. */ | |||
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType) noexcept | |||
{ | |||
auto increment = static_cast<NumericType> (2.0 * double_Pi) * frequency.getNextValue() / sampleRate; | |||
auto value = generator (pos - static_cast<NumericType> (double_Pi)); | |||
pos = std::fmod (pos + increment, static_cast<NumericType> (2.0 * double_Pi)); | |||
return value; | |||
} | |||
/** Processes the input and output buffers supplied in the processing context. */ | |||
template <typename ProcessContext> | |||
void process (const ProcessContext& context) noexcept | |||
{ | |||
auto&& outBlock = context.getOutputBlock(); | |||
// this is an output-only processory | |||
jassert (context.getInputBlock().getNumChannels() == 0 || (! context.usesSeparateInputAndOutputBlocks())); | |||
jassert (outBlock.getNumSamples() <= static_cast<size_t> (rampBuffer.size())); | |||
auto len = outBlock.getNumSamples(); | |||
auto numChannels = outBlock.getNumChannels(); | |||
auto baseIncrement = static_cast<NumericType> (2.0 * double_Pi) / sampleRate; | |||
if (frequency.isSmoothing()) | |||
{ | |||
auto* buffer = rampBuffer.getRawDataPointer(); | |||
for (size_t i = 0; i < len; ++i) | |||
{ | |||
buffer[i] = pos - static_cast<NumericType> (double_Pi); | |||
pos = std::fmod (pos + (baseIncrement * frequency.getNextValue()), static_cast<NumericType> (2.0 * double_Pi)); | |||
} | |||
for (size_t ch = 0; 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(); | |||
for (size_t ch = 0; ch < numChannels; ++ch) | |||
{ | |||
auto p = pos; | |||
auto* dst = outBlock.getChannelPointer (ch); | |||
for (size_t i = 0; i < len; ++i) | |||
{ | |||
dst[i] = generator (p - static_cast<NumericType> (double_Pi)); | |||
p = std::fmod (p + freq, static_cast<NumericType> (2.0 * double_Pi)); | |||
} | |||
} | |||
pos = std::fmod (pos + freq * static_cast<NumericType> (len), static_cast<NumericType> (2.0 * double_Pi)); | |||
} | |||
} | |||
private: | |||
//============================================================================== | |||
std::function<NumericType (NumericType)> generator; | |||
ScopedPointer<LookupTableTransform<NumericType>> lookupTable; | |||
Array<NumericType> rampBuffer; | |||
LinearSmoothedValue<NumericType> frequency {static_cast<NumericType> (440.0)}; | |||
NumericType sampleRate = 48000.0, pos = 0.0; | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,718 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 engines used internally in | |||
the Oversampling class. | |||
*/ | |||
template <typename SampleType> | |||
class OversamplingEngine | |||
{ | |||
public: | |||
//=============================================================================== | |||
OversamplingEngine (size_t newNumChannels, size_t newFactor) | |||
{ | |||
numChannels = newNumChannels; | |||
factor = newFactor; | |||
} | |||
virtual ~OversamplingEngine() {} | |||
//=============================================================================== | |||
virtual SampleType getLatencyInSamples() = 0; | |||
size_t getFactor() { return factor; } | |||
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(); | |||
} | |||
dsp::AudioBlock<SampleType> getProcessedSamples (size_t numSamples) | |||
{ | |||
return dsp::AudioBlock<SampleType> (buffer).getSubBlock (0, numSamples); | |||
} | |||
virtual void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) = 0; | |||
virtual void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) = 0; | |||
protected: | |||
//=============================================================================== | |||
AudioBuffer<SampleType> buffer; | |||
size_t factor; | |||
size_t numChannels; | |||
}; | |||
//=============================================================================== | |||
/** Dummy oversampling engine class which simply copies and pastes the input | |||
signal, which could be equivalent to a "one time" oversampling processing. | |||
*/ | |||
template <typename SampleType> | |||
class OversamplingDummy : public OversamplingEngine<SampleType> | |||
{ | |||
public: | |||
//=============================================================================== | |||
OversamplingDummy (size_t numChannels) : OversamplingEngine<SampleType> (numChannels, 1) {} | |||
~OversamplingDummy() {} | |||
//=============================================================================== | |||
SampleType getLatencyInSamples() override | |||
{ | |||
return 0.f; | |||
} | |||
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override | |||
{ | |||
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels())); | |||
jassert (inputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples())); | |||
for (size_t channel = 0; channel < inputBlock.getNumChannels(); channel++) | |||
OversamplingEngine<SampleType>::buffer.copyFrom (static_cast<int> (channel), 0, | |||
inputBlock.getChannelPointer (channel), static_cast<int> (inputBlock.getNumSamples())); | |||
} | |||
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) override | |||
{ | |||
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels())); | |||
jassert (outputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples())); | |||
outputBlock.copy (OversamplingEngine<SampleType>::getProcessedSamples (outputBlock.getNumSamples())); | |||
} | |||
private: | |||
//=============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OversamplingDummy) | |||
}; | |||
//=============================================================================== | |||
/** Oversampling engine 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> | |||
class Oversampling2TimesEquirippleFIR : public OversamplingEngine<SampleType> | |||
{ | |||
public: | |||
//=============================================================================== | |||
Oversampling2TimesEquirippleFIR (size_t numChannels, | |||
SampleType normalizedTransitionWidthUp, | |||
SampleType stopbandAttenuationdBUp, | |||
SampleType normalizedTransitionWidthDown, | |||
SampleType stopbandAttenuationdBDown) : OversamplingEngine<SampleType> (numChannels, 2) | |||
{ | |||
coefficientsUp = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp); | |||
coefficientsDown = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthDown, stopbandAttenuationdBDown); | |||
auto N = coefficientsUp.getFilterOrder() + 1; | |||
stateUp.setSize (static_cast<int> (numChannels), static_cast<int> (N)); | |||
N = coefficientsDown.getFilterOrder() + 1; | |||
auto Ndiv2 = N / 2; | |||
auto Ndiv4 = Ndiv2 / 2; | |||
stateDown.setSize (static_cast<int> (numChannels), static_cast<int> (N)); | |||
stateDown2.setSize (static_cast<int> (numChannels), static_cast<int> (Ndiv4 + 1)); | |||
position.resize (static_cast<int> (numChannels)); | |||
} | |||
~Oversampling2TimesEquirippleFIR() {} | |||
//=============================================================================== | |||
SampleType getLatencyInSamples() override | |||
{ | |||
return static_cast<SampleType> (coefficientsUp.getFilterOrder() + coefficientsDown.getFilterOrder()) * 0.5f; | |||
} | |||
void reset() override | |||
{ | |||
OversamplingEngine<SampleType>::reset(); | |||
stateUp.clear(); | |||
stateDown.clear(); | |||
stateDown2.clear(); | |||
position.fill (0); | |||
} | |||
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override | |||
{ | |||
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels())); | |||
jassert (inputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::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 = OversamplingEngine<SampleType>::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 (dsp::AudioBlock<SampleType> &outputBlock) override | |||
{ | |||
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels())); | |||
jassert (outputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::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 = OversamplingEngine<SampleType>::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: | |||
//=============================================================================== | |||
dsp::FIR::Coefficients<SampleType> coefficientsUp, coefficientsDown; | |||
AudioBuffer<SampleType> stateUp, stateDown, stateDown2; | |||
Array<size_t> position; | |||
//=============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesEquirippleFIR) | |||
}; | |||
//=============================================================================== | |||
/** Oversampling engine 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> | |||
class Oversampling2TimesPolyphaseIIR : public OversamplingEngine<SampleType> | |||
{ | |||
public: | |||
//=============================================================================== | |||
Oversampling2TimesPolyphaseIIR (size_t numChannels, | |||
SampleType normalizedTransitionWidthUp, | |||
SampleType stopbandAttenuationdBUp, | |||
SampleType normalizedTransitionWidthDown, | |||
SampleType stopbandAttenuationdBDown) : OversamplingEngine<SampleType> (2, numChannels) | |||
{ | |||
auto structureUp = dsp::FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp); | |||
dsp::IIR::Coefficients<SampleType> coeffsUp = getCoefficients (structureUp); | |||
latency = static_cast<SampleType> (-(coeffsUp.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * 2 * double_Pi)); | |||
auto structureDown = dsp::FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalizedTransitionWidthDown, stopbandAttenuationdBDown); | |||
dsp::IIR::Coefficients<SampleType> coeffsDown = getCoefficients (structureDown); | |||
latency += static_cast<SampleType> (-(coeffsDown.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * 2 * double_Pi)); | |||
for (auto i = 0; i < structureUp.directPath.size(); i++) | |||
coefficientsUp.add (structureUp.directPath[i].coefficients[0]); | |||
for (auto i = 1; i < structureUp.delayedPath.size(); i++) | |||
coefficientsUp.add (structureUp.delayedPath[i].coefficients[0]); | |||
for (auto i = 0; i < structureDown.directPath.size(); i++) | |||
coefficientsDown.add (structureDown.directPath[i].coefficients[0]); | |||
for (auto i = 1; i < structureDown.delayedPath.size(); i++) | |||
coefficientsDown.add (structureDown.delayedPath[i].coefficients[0]); | |||
v1Up.setSize (static_cast<int> (numChannels), coefficientsUp.size()); | |||
v1Down.setSize (static_cast<int> (numChannels), coefficientsDown.size()); | |||
delayDown.resize (static_cast<int> (numChannels)); | |||
} | |||
~Oversampling2TimesPolyphaseIIR() {} | |||
//=============================================================================== | |||
SampleType getLatencyInSamples() override | |||
{ | |||
return latency; | |||
} | |||
void reset() override | |||
{ | |||
OversamplingEngine<SampleType>::reset(); | |||
v1Up.clear(); | |||
v1Down.clear(); | |||
delayDown.fill (0); | |||
} | |||
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override | |||
{ | |||
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels())); | |||
jassert (inputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::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 = OversamplingEngine<SampleType>::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; | |||
} | |||
} | |||
// Snap To Zero | |||
snapToZero (true); | |||
} | |||
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) override | |||
{ | |||
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels())); | |||
jassert (outputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::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 = OversamplingEngine<SampleType>::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); | |||
} | |||
// Snap To Zero | |||
snapToZero (false); | |||
} | |||
void snapToZero (bool snapUpProcessing) | |||
{ | |||
if (snapUpProcessing) | |||
{ | |||
for (auto channel = 0; channel < OversamplingEngine<SampleType>::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 < OversamplingEngine<SampleType>::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. | |||
*/ | |||
const dsp::IIR::Coefficients<SampleType> getCoefficients (typename dsp::FilterDesign<SampleType>::IIRPolyphaseAllpassStructure &structure) const | |||
{ | |||
dsp::Polynomial<SampleType> numerator1 ({ static_cast<SampleType> (1.0) }); | |||
dsp::Polynomial<SampleType> denominator1 ({ static_cast<SampleType> (1.0) }); | |||
dsp::Polynomial<SampleType> numerator2 ({ static_cast<SampleType> (1.0) }); | |||
dsp::Polynomial<SampleType> denominator2 ({ static_cast<SampleType> (1.0) }); | |||
dsp::Polynomial<SampleType> temp; | |||
for (auto n = 0; n < structure.directPath.size(); n++) | |||
{ | |||
auto *coeffs = structure.directPath.getReference (n).getRawCoefficients(); | |||
if (structure.directPath[n].getFilterOrder() == 1) | |||
{ | |||
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1] }); | |||
numerator1 = numerator1.getProductWith (temp); | |||
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[2] }); | |||
denominator1 = denominator1.getProductWith (temp); | |||
} | |||
else | |||
{ | |||
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] }); | |||
numerator1 = numerator1.getProductWith (temp); | |||
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[3], coeffs[4] }); | |||
denominator1 = denominator1.getProductWith (temp); | |||
} | |||
} | |||
for (auto n = 0; n < structure.delayedPath.size(); n++) | |||
{ | |||
auto *coeffs = structure.delayedPath.getReference (n).getRawCoefficients(); | |||
if (structure.delayedPath[n].getFilterOrder() == 1) | |||
{ | |||
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1] }); | |||
numerator2 = numerator2.getProductWith (temp); | |||
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[2] }); | |||
denominator2 = denominator2.getProductWith (temp); | |||
} | |||
else | |||
{ | |||
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] }); | |||
numerator2 = numerator2.getProductWith (temp); | |||
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[3], coeffs[4] }); | |||
denominator2 = denominator2.getProductWith (temp); | |||
} | |||
} | |||
dsp::Polynomial<SampleType> numeratorf1 = numerator1.getProductWith (denominator2); | |||
dsp::Polynomial<SampleType> numeratorf2 = numerator2.getProductWith (denominator1); | |||
dsp::Polynomial<SampleType> numerator = numeratorf1.getSumWith (numeratorf2); | |||
dsp::Polynomial<SampleType> denominator = denominator1.getProductWith (denominator2); | |||
dsp::IIR::Coefficients<SampleType> coeffs; | |||
coeffs.coefficients.clear(); | |||
auto inversion = static_cast<SampleType> (1.0) / denominator[0]; | |||
for (auto i = 0; i <= numerator.getOrder(); i++) | |||
coeffs.coefficients.add (numerator[i] * inversion); | |||
for (auto 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, size_t newFactor, FilterType newType, bool newMaxQuality) | |||
{ | |||
jassert (newFactor >= 0 && newFactor <= 4 && newNumChannels > 0); | |||
factorOversampling = static_cast<size_t> (1) << newFactor; | |||
isMaximumQuality = newMaxQuality; | |||
type = newType; | |||
numChannels = newNumChannels; | |||
if (newFactor == 0) | |||
{ | |||
numStages = 1; | |||
engines.add (new OversamplingDummy<SampleType> (numChannels)); | |||
} | |||
else if (type == FilterType::filterHalfBandPolyphaseIIR) | |||
{ | |||
numStages = newFactor; | |||
for (size_t n = 0; n < numStages; n++) | |||
{ | |||
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.f); | |||
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.f); | |||
auto gaindBStartUp = (isMaximumQuality ? -75.f : -65.f); | |||
auto gaindBStartDown = (isMaximumQuality ? -70.f : -60.f); | |||
auto gaindBFactorUp = (isMaximumQuality ? 10.f : 8.f); | |||
auto gaindBFactorDown = (isMaximumQuality ? 10.f : 8.f); | |||
engines.add (new Oversampling2TimesPolyphaseIIR<SampleType> (numChannels, | |||
twUp, gaindBStartUp + gaindBFactorUp * n, | |||
twDown, gaindBStartDown + gaindBFactorDown * n)); | |||
} | |||
} | |||
else if (type == FilterType::filterHalfBandFIREquiripple) | |||
{ | |||
numStages = newFactor; | |||
for (size_t n = 0; n < numStages; n++) | |||
{ | |||
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.f); | |||
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.f); | |||
auto gaindBStartUp = (isMaximumQuality ? -90.f : -70.f); | |||
auto gaindBStartDown = (isMaximumQuality ? -70.f : -60.f); | |||
auto gaindBFactorUp = (isMaximumQuality ? 10.f : 8.f); | |||
auto gaindBFactorDown = (isMaximumQuality ? 10.f : 8.f); | |||
engines.add (new Oversampling2TimesEquirippleFIR<SampleType> (numChannels, | |||
twUp, gaindBStartUp + gaindBFactorUp * n, | |||
twDown, gaindBStartDown + gaindBFactorDown * n)); | |||
} | |||
} | |||
} | |||
template <typename SampleType> | |||
Oversampling<SampleType>::~Oversampling() | |||
{ | |||
engines.clear(); | |||
} | |||
//=============================================================================== | |||
template <typename SampleType> | |||
SampleType Oversampling<SampleType>::getLatencyInSamples() noexcept | |||
{ | |||
auto latency = static_cast<SampleType> (0); | |||
size_t order = 1; | |||
for (size_t n = 0; n < numStages; n++) | |||
{ | |||
auto& engine = *engines[static_cast<int> (n)]; | |||
order *= engine.getFactor(); | |||
latency += engine.getLatencyInSamples() / static_cast<SampleType> (order); | |||
} | |||
return latency; | |||
} | |||
template <typename SampleType> | |||
size_t Oversampling<SampleType>::getOversamplingFactor() noexcept | |||
{ | |||
return factorOversampling; | |||
} | |||
//=============================================================================== | |||
template <typename SampleType> | |||
void Oversampling<SampleType>::initProcessing (size_t maximumNumberOfSamplesBeforeOversampling) | |||
{ | |||
jassert (engines.size() > 0); | |||
auto currentNumSamples = maximumNumberOfSamplesBeforeOversampling; | |||
for (size_t n = 0; n < numStages; n++) | |||
{ | |||
auto& engine = *engines[static_cast<int> (n)]; | |||
engine.initProcessing (currentNumSamples); | |||
currentNumSamples *= engine.getFactor(); | |||
} | |||
isReady = true; | |||
reset(); | |||
} | |||
template <typename SampleType> | |||
void Oversampling<SampleType>::reset() noexcept | |||
{ | |||
jassert (engines.size() > 0); | |||
if (isReady) | |||
for (auto n = 0; n < engines.size(); n++) | |||
engines[n]->reset(); | |||
} | |||
template <typename SampleType> | |||
typename dsp::AudioBlock<SampleType> Oversampling<SampleType>::processSamplesUp (const dsp::AudioBlock<SampleType> &inputBlock) noexcept | |||
{ | |||
jassert (engines.size() > 0); | |||
if (! isReady) | |||
return dsp::AudioBlock<SampleType>(); | |||
dsp::AudioBlock<SampleType> audioBlock = inputBlock; | |||
for (size_t n = 0; n < numStages; n++) | |||
{ | |||
auto& engine = *engines[static_cast<int> (n)]; | |||
engine.processSamplesUp (audioBlock); | |||
audioBlock = engine.getProcessedSamples (audioBlock.getNumSamples() * engine.getFactor()); | |||
} | |||
return audioBlock; | |||
} | |||
template <typename SampleType> | |||
void Oversampling<SampleType>::processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) noexcept | |||
{ | |||
jassert (engines.size() > 0); | |||
if (! isReady) | |||
return; | |||
auto currentNumSamples = outputBlock.getNumSamples(); | |||
for (size_t n = 0; n < numStages - 1; n++) | |||
currentNumSamples *= engines[static_cast<int> (n)]->getFactor(); | |||
for (size_t n = numStages - 1; n > 0; n--) | |||
{ | |||
auto& engine = *engines[static_cast<int> (n)]; | |||
auto audioBlock = engines[static_cast<int> (n - 1)]->getProcessedSamples (currentNumSamples); | |||
engine.processSamplesDown (audioBlock); | |||
currentNumSamples /= engine.getFactor(); | |||
} | |||
engines[static_cast<int> (0)]->processSamplesDown (outputBlock); | |||
} | |||
template class Oversampling<float>; | |||
template class Oversampling<double>; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,146 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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> | |||
class OversamplingEngine; | |||
//=============================================================================== | |||
/** | |||
A processing class performing multi-channel oversampling. | |||
It can be configured to do 2 times, 4 times, 8 times or 16 times oversampling | |||
using a multi-stage approach, either polyphase allpass IIR filters or FIR | |||
filters for the filtering, and reports successfully the latency added by the | |||
filter stages. | |||
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 N times the input signal, processing the upsampling signal | |||
with the increased internal sample rate, and downsample the result to get | |||
back the original processing sample rate. | |||
Choose between FIR or IIR filtering depending on your needs in term of | |||
latency and phase distortion. With FIR filters, the phase is linear but the | |||
latency is maximum. With IIR filtering, the phase is compromised around the | |||
Nyquist frequency but the phase is minimum. | |||
@see FilterDesign. | |||
*/ | |||
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 | |||
}; | |||
//=============================================================================== | |||
/** | |||
Constructor of the oversampling class. All the processing parameters must be | |||
provided at the creation of the oversampling object. | |||
Note : you might want to create a class heriting from Oversampling with a | |||
different constructor if you need more control on what happens in the process. | |||
@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, | |||
the filters will be more efficient, but the CPU load will | |||
increase as well | |||
*/ | |||
Oversampling (size_t numChannels, size_t factor, FilterType type, bool isMaxQuality = true); | |||
/** Destructor. */ | |||
~Oversampling(); | |||
//=============================================================================== | |||
/** Returns the latency in samples of the whole processing. Use this information | |||
in your main processor to compensate the additional latency involved with | |||
the oversampling, for example with a dry / wet functionality, and to report | |||
the latency to the DAW. | |||
Note : the latency might not be integer, so you might need to round its value | |||
or to compensate it properly in your processing code. | |||
*/ | |||
SampleType getLatencyInSamples() noexcept; | |||
/** Returns the current oversampling factor. */ | |||
size_t getOversamplingFactor() 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. | |||
*/ | |||
dsp::AudioBlock<SampleType> processSamplesUp (const dsp::AudioBlock<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 (dsp::AudioBlock<SampleType> &outputBlock) noexcept; | |||
private: | |||
//=============================================================================== | |||
bool isMaximumQuality; | |||
size_t factorOversampling, numStages; | |||
FilterType type; | |||
size_t numChannels; | |||
//=============================================================================== | |||
bool isReady = false; | |||
OwnedArray<OversamplingEngine<SampleType>> engines; | |||
//=============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling) | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,162 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
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 procoessors. This is useful to for | |||
multi-mono filters which share the same state among several mono processors. | |||
*/ | |||
struct ProcessorState : 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 | |||
*/ | |||
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>; | |||
/** 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 AudioBlockType& getInputBlock() const noexcept { return ioBlock; } | |||
/** Returns the audio block to use as the output to a process function. */ | |||
AudioBlockType& getOutputBlock() const noexcept { return const_cast<AudioBlockType&> (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; | |||
}; | |||
//============================================================================== | |||
/** | |||
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 | |||
*/ | |||
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>; | |||
/** 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 AudioBlockType& input, AudioBlockType& output) noexcept | |||
: inputBlock (input), outputBlock (output) {} | |||
ProcessContextNonReplacing (const ProcessContextNonReplacing&) = default; | |||
ProcessContextNonReplacing (ProcessContextNonReplacing&&) = default; | |||
/** Returns the audio block to use as the input to a process function. */ | |||
const AudioBlockType& getInputBlock() const noexcept { return inputBlock; } | |||
/** Returns the audio block to use as the output to a process function. */ | |||
AudioBlockType& getOutputBlock() const noexcept { return const_cast<AudioBlockType&> (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: | |||
const AudioBlockType& inputBlock; | |||
AudioBlockType& outputBlock; | |||
}; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,121 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 ProcessorHelpers // Internal helper classes used in building the ProcessorChain | |||
{ | |||
template <int arg> | |||
struct GetterHelper | |||
{ | |||
template <typename ProcessorType> | |||
static auto& get (ProcessorType& a) noexcept { return GetterHelper<arg - 1>::get (a.processors); } | |||
}; | |||
template <> | |||
struct GetterHelper<0> | |||
{ | |||
template <typename ProcessorType> | |||
static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); } | |||
}; | |||
template <typename Processor, typename Subclass> | |||
struct ChainBase | |||
{ | |||
Processor processor; | |||
Processor& getProcessor() noexcept { return processor; } | |||
Subclass& getThis() noexcept { return *static_cast<Subclass*> (this); } | |||
template <int arg> auto& get() noexcept { return GetterHelper<arg>::get (getThis()); } | |||
}; | |||
template <typename FirstProcessor, typename... SubsequentProcessors> | |||
struct Chain : public ChainBase<FirstProcessor, Chain<FirstProcessor, SubsequentProcessors...>> | |||
{ | |||
using Base = ChainBase<FirstProcessor, Chain<FirstProcessor, SubsequentProcessors...>>; | |||
void prepare (const ProcessSpec& spec) | |||
{ | |||
Base::processor.prepare (spec); | |||
processors.prepare (spec); | |||
} | |||
template <typename ProcessContext> | |||
void process (ProcessContext& context) noexcept | |||
{ | |||
Base::processor.process (context); | |||
processors.process (context); | |||
} | |||
void reset() | |||
{ | |||
Base::processor.reset(); | |||
processors.reset(); | |||
} | |||
Chain<SubsequentProcessors...> processors; | |||
}; | |||
template <typename ProcessorType> | |||
struct Chain<ProcessorType> : public ChainBase<ProcessorType, Chain<ProcessorType>> | |||
{ | |||
using Base = ChainBase<ProcessorType, Chain<ProcessorType>>; | |||
template <typename ProcessContext> | |||
void process (ProcessContext& context) noexcept | |||
{ | |||
Base::processor.process (context); | |||
} | |||
void prepare (const ProcessSpec& spec) | |||
{ | |||
Base::processor.prepare (spec); | |||
} | |||
void reset() | |||
{ | |||
Base::processor.reset(); | |||
} | |||
}; | |||
} | |||
#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. | |||
*/ | |||
template <typename... Processors> | |||
using ProcessorChain = ProcessorHelpers::Chain<Processors...>; | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,97 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
template <typename MonoProcessorType, typename StateType> | |||
struct ProcessorDuplicator | |||
{ | |||
ProcessorDuplicator() : state (new StateType()) {} | |||
ProcessorDuplicator (StateType* stateToUse) : state (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::AudioBlockType 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 |
@@ -0,0 +1,78 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
*/ | |||
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 | |||
*/ | |||
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 |
@@ -0,0 +1,214 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
/** | |||
An IIR filter that can perform low, band and high-pass filtering on an audio | |||
signal, with 12 dB of attenuation / 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, its gain can be | |||
higher than 0 dB. For the classic 0 dB bandpass, we need to multiply the | |||
result with R2 | |||
*/ | |||
namespace StateVariableFilter | |||
{ | |||
template <typename NumericType> | |||
struct Parameters; | |||
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; | |||
//============================================================================== | |||
/** Creates a filter with default parameters. */ | |||
Filter() : parameters (new Parameters<NumericType>) { reset(); } | |||
Filter (Parameters<NumericType>* paramtersToUse) : parameters (paramtersToUse) { reset(); } | |||
/** 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 called 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"); | |||
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<Parameters<NumericType>::Type::lowPass> (src, dst, n); break; | |||
case Parameters<NumericType>::Type::bandPass: processBlock<Parameters<NumericType>::Type::bandPass> (src, dst, n); break; | |||
case Parameters<NumericType>::Type::highPass: processBlock<Parameters<NumericType>::Type::highPass> (src, dst, n); break; | |||
default: jassertfalse; | |||
} | |||
} | |||
/** 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<Parameters<NumericType>::Type::lowPass> (sample, *parameters); break; | |||
case Parameters<NumericType>::Type::bandPass: return processLoop<Parameters<NumericType>::Type::bandPass> (sample, *parameters); break; | |||
case Parameters<NumericType>::Type::highPass: return processLoop<Parameters<NumericType>::Type::highPass> (sample, *parameters); break; | |||
default: jassertfalse; | |||
} | |||
return SampleType{0}; | |||
} | |||
private: | |||
//============================================================================== | |||
template <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 y[static_cast<size_t> (type)]; | |||
} | |||
template <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<type> (input[i], state); | |||
snapToZero(); | |||
*parameters = state; | |||
} | |||
//============================================================================== | |||
std::array<SampleType, 3> y; | |||
SampleType s1, s2; | |||
//============================================================================== | |||
JUCE_LEAK_DETECTOR (Filter) | |||
}; | |||
//============================================================================== | |||
template <typename NumericType> | |||
struct Parameters : public ProcessorState | |||
{ | |||
//============================================================================== | |||
enum class Type | |||
{ | |||
lowPass, | |||
bandPass, | |||
highPass | |||
}; | |||
//============================================================================== | |||
/** 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 / std::sqrt (2.0))) noexcept | |||
{ | |||
g = static_cast<NumericType> (std::tan (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 (double_Pi * 200.0 / 44100.0)); | |||
NumericType R2 = static_cast<NumericType> (std::sqrt (2.0)); | |||
NumericType h = static_cast<NumericType> (1.0 / (1.0 + R2 * g + g * g)); | |||
}; | |||
} | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,72 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
*/ | |||
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 | |||
{ | |||
jassert (context.getInputBlock().getNumChannels() == context.getOutputBlock().getNumChannels()); | |||
jassert (context.getInputBlock().getNumSamples() == context.getOutputBlock().getNumSamples()); | |||
AudioBlock<FloatType>::process (context.getInputBlock(), | |||
context.getOutputBlock(), | |||
functionToUse); | |||
} | |||
void reset() noexcept {} | |||
}; | |||
//============================================================================== | |||
template <typename Functor> | |||
static WaveShaper<typename std::result_of<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; } | |||
} // namespace dsp | |||
} // namespace juce |
@@ -0,0 +1,158 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Stores a 3D orientation, which can be rotated by dragging with the mouse. | |||
*/ | |||
class Draggable3DOrientation | |||
{ | |||
public: | |||
typedef Vector3D<float> VectorType; | |||
typedef Quaternion<float> QuaternionType; | |||
/** Creates a Draggable3DOrientation, initially set up to be aligned along the X axis. */ | |||
Draggable3DOrientation (float objectRadius = 0.5f) noexcept | |||
: radius (jmax (0.1f, objectRadius)), | |||
quaternion (VectorType::xAxis(), 0) | |||
{ | |||
} | |||
/** Creates a Draggable3DOrientation from a user-supplied quaternion. */ | |||
Draggable3DOrientation (const Quaternion<float>& quaternionToUse, | |||
float objectRadius = 0.5f) noexcept | |||
: radius (jmax (0.1f, objectRadius)), | |||
quaternion (quaternionToUse) | |||
{ | |||
} | |||
/** Resets the orientation, specifying the axis to align it along. */ | |||
void reset (const VectorType& axis) noexcept | |||
{ | |||
quaternion = QuaternionType (axis, 0); | |||
} | |||
/** Sets the viewport area within which mouse-drag positions will occur. | |||
You'll need to set this rectangle before calling mouseDown. The centre of the | |||
rectangle is assumed to be the centre of the object that will be rotated, and | |||
the size of the rectangle will be used to scale the object radius - see setRadius(). | |||
*/ | |||
void setViewport (const Rectangle<int>& newArea) noexcept | |||
{ | |||
area = newArea; | |||
} | |||
/** Sets the size of the rotated object, as a proportion of the viewport's size. | |||
@see setViewport | |||
*/ | |||
void setRadius (float newRadius) noexcept | |||
{ | |||
radius = jmax (0.1f, newRadius); | |||
} | |||
/** Begins a mouse-drag operation. | |||
You must call this before any calls to mouseDrag(). The position that is supplied | |||
will be treated as being relative to the centre of the rectangle passed to setViewport(). | |||
*/ | |||
template <typename Type> | |||
void mouseDown (Point<Type> mousePos) noexcept | |||
{ | |||
lastMouse = mousePosToProportion (mousePos.toFloat()); | |||
} | |||
/** Continues a mouse-drag operation. | |||
After calling mouseDown() to begin a drag sequence, you can call this method | |||
to continue it. | |||
*/ | |||
template <typename Type> | |||
void mouseDrag (Point<Type> mousePos) noexcept | |||
{ | |||
const VectorType oldPos (projectOnSphere (lastMouse)); | |||
lastMouse = mousePosToProportion (mousePos.toFloat()); | |||
const VectorType newPos (projectOnSphere (lastMouse)); | |||
quaternion *= rotationFromMove (oldPos, newPos); | |||
} | |||
/** Returns the matrix that should be used to apply the current orientation. | |||
@see applyToOpenGLMatrix | |||
*/ | |||
Matrix3D<float> getRotationMatrix() const noexcept | |||
{ | |||
return quaternion.getRotationMatrix(); | |||
} | |||
/** Provides direct access to the quaternion. */ | |||
QuaternionType& getQuaternion() noexcept | |||
{ | |||
return quaternion; | |||
} | |||
private: | |||
Rectangle<int> area; | |||
float radius; | |||
QuaternionType quaternion; | |||
Point<float> lastMouse; | |||
Point<float> mousePosToProportion (const Point<float> mousePos) const noexcept | |||
{ | |||
const int scale = (jmin (area.getWidth(), area.getHeight()) / 2); | |||
// You must call setViewport() to give this object a valid window size before | |||
// calling any of the mouse input methods! | |||
jassert (scale > 0); | |||
return Point<float> ((mousePos.x - (float) area.getCentreX()) / (float) scale, | |||
((float) area.getCentreY() - mousePos.y) / (float) scale); | |||
} | |||
VectorType projectOnSphere (const Point<float> pos) const noexcept | |||
{ | |||
const float radiusSquared = radius * radius; | |||
const float xySquared = pos.x * pos.x + pos.y * pos.y; | |||
return VectorType (pos.x, pos.y, | |||
xySquared < radiusSquared * 0.5f ? std::sqrt (radiusSquared - xySquared) | |||
: (radiusSquared / (2.0f * std::sqrt (xySquared)))); | |||
} | |||
QuaternionType rotationFromMove (const VectorType& from, const VectorType& to) const noexcept | |||
{ | |||
VectorType rotationAxis (to ^ from); | |||
if (rotationAxis.lengthIsBelowEpsilon()) | |||
rotationAxis = VectorType::xAxis(); | |||
const float d = jlimit (-1.0f, 1.0f, (from - to).length() / (2.0f * radius)); | |||
return QuaternionType::fromAngle (2.0f * std::asin (d), rotationAxis); | |||
} | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,155 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
A 4x4 3D transformation matrix. | |||
@see Vector3D, Quaternion, AffineTransform | |||
*/ | |||
template <typename Type> | |||
class Matrix3D | |||
{ | |||
public: | |||
/** Creates an identity matrix. */ | |||
Matrix3D() noexcept | |||
{ | |||
mat[0] = (Type) 1; mat[1] = 0; mat[2] = 0; mat[3] = 0; | |||
mat[4] = 0; mat[5] = (Type) 1; mat[6] = 0; mat[7] = 0; | |||
mat[8] = 0; mat[9] = 0; mat[10] = (Type) 1; mat[11] = 0; | |||
mat[12] = 0; mat[13] = 0; mat[14] = 0; mat[15] = (Type) 1; | |||
} | |||
/** Creates a copy of another matrix. */ | |||
Matrix3D (const Matrix3D& other) noexcept | |||
{ | |||
memcpy (mat, other.mat, sizeof (mat)); | |||
} | |||
/** Copies another matrix. */ | |||
Matrix3D& operator= (const Matrix3D& other) noexcept | |||
{ | |||
memcpy (mat, other.mat, sizeof (mat)); | |||
return *this; | |||
} | |||
/** Creates a matrix from its raw 4x4 values. */ | |||
Matrix3D (const Type& m00, const Type& m10, const Type& m20, const Type& m30, | |||
const Type& m01, const Type& m11, const Type& m21, const Type& m31, | |||
const Type& m02, const Type& m12, const Type& m22, const Type& m32, | |||
const Type& m03, const Type& m13, const Type& m23, const Type& m33) noexcept | |||
{ | |||
mat[0] = m00; mat[1] = m10; mat[2] = m20; mat[3] = m30; | |||
mat[4] = m01; mat[5] = m11; mat[6] = m21; mat[7] = m31; | |||
mat[8] = m02; mat[9] = m12; mat[10] = m22; mat[11] = m32; | |||
mat[12] = m03; mat[13] = m13; mat[14] = m23; mat[15] = m33; | |||
} | |||
/** Creates a matrix from an array of 16 raw values. */ | |||
Matrix3D (const Type* values) noexcept | |||
{ | |||
memcpy (mat, values, sizeof (mat)); | |||
} | |||
/** Creates a matrix from a 2D affine transform. */ | |||
Matrix3D (const AffineTransform& transform) noexcept | |||
{ | |||
mat[0] = transform.mat00; mat[1] = transform.mat10; mat[2] = 0; mat[3] = 0; | |||
mat[4] = transform.mat01; mat[5] = transform.mat11; mat[6] = 0; mat[7] = 0; | |||
mat[8] = 0; mat[9] = 0; mat[10] = (Type) 1; mat[11] = 0; | |||
mat[12] = transform.mat02; mat[13] = transform.mat12; mat[14] = 0; mat[15] = (Type) 1; | |||
} | |||
/** Creates a matrix from a 3D vector translation. */ | |||
Matrix3D (Vector3D<Type> vector) noexcept | |||
{ | |||
mat[0] = (Type) 1; mat[1] = 0; mat[2] = 0; mat[3] = 0; | |||
mat[4] = 0; mat[5] = (Type) 1; mat[6] = 0; mat[7] = 0; | |||
mat[8] = 0; mat[9] = 0; mat[10] = (Type) 1; mat[11] = 0; | |||
mat[12] = vector.x; mat[13] = vector.y; mat[14] = vector.z; mat[15] = (Type) 1; | |||
} | |||
/** Returns a new matrix from the given frustrum values. */ | |||
static Matrix3D fromFrustum (Type left, Type right, Type bottom, Type top, Type nearDistance, Type farDistance) noexcept | |||
{ | |||
return Matrix3D ((2.0f * nearDistance) / (right - left), 0.0f, 0.0f, 0.0f, | |||
0.0f, (2.0f * nearDistance) / (top - bottom), 0.0f, 0.0f, | |||
(right + left) / (right - left), (top + bottom) / (top - bottom), -(farDistance + nearDistance) / (farDistance - nearDistance), -1.0f, | |||
0.0f, 0.0f, -(2.0f * farDistance * nearDistance) / (farDistance - nearDistance), 0.0f); | |||
} | |||
/** Multiplies this matrix by another. */ | |||
Matrix3D& operator*= (const Matrix3D& other) noexcept | |||
{ | |||
return *this = *this * other; | |||
} | |||
/** Multiplies this matrix by another, and returns the result. */ | |||
Matrix3D operator* (const Matrix3D& other) const noexcept | |||
{ | |||
const Type* const m2 = other.mat; | |||
return Matrix3D (mat[0] * m2[0] + mat[1] * m2[4] + mat[2] * m2[8] + mat[3] * m2[12], | |||
mat[0] * m2[1] + mat[1] * m2[5] + mat[2] * m2[9] + mat[3] * m2[13], | |||
mat[0] * m2[2] + mat[1] * m2[6] + mat[2] * m2[10] + mat[3] * m2[14], | |||
mat[0] * m2[3] + mat[1] * m2[7] + mat[2] * m2[11] + mat[3] * m2[15], | |||
mat[4] * m2[0] + mat[5] * m2[4] + mat[6] * m2[8] + mat[7] * m2[12], | |||
mat[4] * m2[1] + mat[5] * m2[5] + mat[6] * m2[9] + mat[7] * m2[13], | |||
mat[4] * m2[2] + mat[5] * m2[6] + mat[6] * m2[10] + mat[7] * m2[14], | |||
mat[4] * m2[3] + mat[5] * m2[7] + mat[6] * m2[11] + mat[7] * m2[15], | |||
mat[8] * m2[0] + mat[9] * m2[4] + mat[10] * m2[8] + mat[11] * m2[12], | |||
mat[8] * m2[1] + mat[9] * m2[5] + mat[10] * m2[9] + mat[11] * m2[13], | |||
mat[8] * m2[2] + mat[9] * m2[6] + mat[10] * m2[10] + mat[11] * m2[14], | |||
mat[8] * m2[3] + mat[9] * m2[7] + mat[10] * m2[11] + mat[11] * m2[15], | |||
mat[12] * m2[0] + mat[13] * m2[4] + mat[14] * m2[8] + mat[15] * m2[12], | |||
mat[12] * m2[1] + mat[13] * m2[5] + mat[14] * m2[9] + mat[15] * m2[13], | |||
mat[12] * m2[2] + mat[13] * m2[6] + mat[14] * m2[10] + mat[15] * m2[14], | |||
mat[12] * m2[3] + mat[13] * m2[7] + mat[14] * m2[11] + mat[15] * m2[15]); | |||
} | |||
/** Returns a copy of this matrix after rotation through the Y, X and then Z angles | |||
specified by the vector. | |||
*/ | |||
Matrix3D rotated (Vector3D<Type> eulerAngleRadians) const noexcept | |||
{ | |||
const Type cx = std::cos (eulerAngleRadians.x), sx = std::sin (eulerAngleRadians.x), | |||
cy = std::cos (eulerAngleRadians.y), sy = std::sin (eulerAngleRadians.y), | |||
cz = std::cos (eulerAngleRadians.z), sz = std::sin (eulerAngleRadians.z); | |||
return Matrix3D ((cy * cz) + (sx * sy * sz), cx * sz, (cy * sx * sz) - (cz * sy), 0.0f, | |||
(cz * sx * sy) - (cy * sz), cx * cz, (cy * cz * sx) + (sy * sz), 0.0f, | |||
cx * sy, -sx, cx * cy, 0.0f, | |||
0.0f, 0.0f, 0.0f, 1.0f); | |||
} | |||
/** The 4x4 matrix values. These are stored in the standard OpenGL order. */ | |||
Type mat[16]; | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,97 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Holds a quaternion (a 3D vector and a scalar value). | |||
*/ | |||
template <typename Type> | |||
class Quaternion | |||
{ | |||
public: | |||
Quaternion() noexcept : scalar() {} | |||
Quaternion (const Quaternion& other) noexcept : vector (other.vector), scalar (other.scalar) {} | |||
Quaternion (Vector3D<Type> vectorPart, Type scalarPart) noexcept : vector (vectorPart), scalar (scalarPart) {} | |||
Quaternion (Type x, Type y, Type z, Type w) noexcept : vector (x, y, z), scalar (w) {} | |||
/** Creates a quaternion from an angle and an axis. */ | |||
static Quaternion fromAngle (Type angle, Vector3D<Type> axis) noexcept | |||
{ | |||
return Quaternion (axis.normalised() * std::sin (angle / (Type) 2), std::cos (angle / (Type) 2)); | |||
} | |||
Quaternion& operator= (Quaternion other) noexcept | |||
{ | |||
vector = other.vector; | |||
scalar = other.scalar; | |||
return *this; | |||
} | |||
Quaternion& operator*= (Quaternion other) noexcept | |||
{ | |||
const Type oldScalar (scalar); | |||
scalar = (scalar * other.scalar) - (vector * other.vector); | |||
vector = (other.vector * oldScalar) + (vector * other.scalar) + (vector ^ other.vector); | |||
return *this; | |||
} | |||
Type length() const noexcept { return std::sqrt (normal()); } | |||
Type normal() const noexcept { return scalar * scalar + vector.lengthSquared(); } | |||
Quaternion normalised() const noexcept | |||
{ | |||
const Type len (length()); | |||
jassert (len > 0); | |||
return Quaternion (vector / len, scalar / len); | |||
} | |||
/** Returns the matrix that will perform the rotation specified by this quaternion. */ | |||
Matrix3D<Type> getRotationMatrix() const noexcept | |||
{ | |||
const Type norm (normal()); | |||
const Type s (norm > 0 ? ((Type) 2) / norm : 0); | |||
const Type xs (s * vector.x), ys (s * vector.y), zs (s * vector.z); | |||
const Type wx (xs * scalar), wy (ys * scalar), wz (zs * scalar); | |||
const Type xx (xs * vector.x), xy (ys * vector.x), xz (zs * vector.x); | |||
const Type yy (ys * vector.y), yz (zs * vector.y), zz (zs * vector.z); | |||
return Matrix3D<Type> (((Type) 1) - (yy + zz), xy - wz, xz + wy, 0, | |||
xy + wz, ((Type) 1) - (xx+ zz), yz - wx, 0, | |||
xz - wy, yz + wx, ((Type) 1) - (xx + yy), 0, | |||
0, 0, 0, (Type) 1); | |||
} | |||
/** The vector part of the quaternion. */ | |||
Vector3D<Type> vector; | |||
/** The scalar part of the quaternion. */ | |||
Type scalar; | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,82 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
A three-coordinate vector. | |||
*/ | |||
template <typename Type> | |||
class Vector3D | |||
{ | |||
public: | |||
Vector3D() noexcept : x(), y(), z() {} | |||
Vector3D (Type xValue, Type yValue, Type zValue) noexcept : x (xValue), y (yValue), z (zValue) {} | |||
Vector3D (const Vector3D& other) noexcept : x (other.x), y (other.y), z (other.z) {} | |||
Vector3D& operator= (Vector3D other) noexcept { x = other.x; y = other.y; z = other.z; return *this; } | |||
/** Returns a vector that lies along the X axis. */ | |||
static Vector3D xAxis() noexcept { return Vector3D ((Type) 1, 0, 0); } | |||
/** Returns a vector that lies along the Y axis. */ | |||
static Vector3D yAxis() noexcept { return Vector3D (0, (Type) 1, 0); } | |||
/** Returns a vector that lies along the Z axis. */ | |||
static Vector3D zAxis() noexcept { return Vector3D (0, 0, (Type) 1); } | |||
Vector3D& operator+= (Vector3D other) noexcept { x += other.x; y += other.y; z += other.z; return *this; } | |||
Vector3D& operator-= (Vector3D other) noexcept { x -= other.x; y -= other.y; z -= other.z; return *this; } | |||
Vector3D& operator*= (Type scaleFactor) noexcept { x *= scaleFactor; y *= scaleFactor; z *= scaleFactor; return *this; } | |||
Vector3D& operator/= (Type scaleFactor) noexcept { x /= scaleFactor; y /= scaleFactor; z /= scaleFactor; return *this; } | |||
Vector3D operator+ (Vector3D other) const noexcept { return Vector3D (x + other.x, y + other.y, z + other.z); } | |||
Vector3D operator- (Vector3D other) const noexcept { return Vector3D (x - other.x, y - other.y, z - other.z); } | |||
Vector3D operator* (Type scaleFactor) const noexcept { return Vector3D (x * scaleFactor, y * scaleFactor, z * scaleFactor); } | |||
Vector3D operator/ (Type scaleFactor) const noexcept { return Vector3D (x / scaleFactor, y / scaleFactor, z / scaleFactor); } | |||
Vector3D operator-() const noexcept { return Vector3D (-x, -y, -z); } | |||
/** Returns the dot-product of these two vectors. */ | |||
Type operator* (Vector3D other) const noexcept { return x * other.x + y * other.y + z * other.z; } | |||
/** Returns the cross-product of these two vectors. */ | |||
Vector3D operator^ (Vector3D other) const noexcept { return Vector3D (y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); } | |||
Type length() const noexcept { return std::sqrt (lengthSquared()); } | |||
Type lengthSquared() const noexcept { return x * x + y * y + z * z; } | |||
Vector3D normalised() const noexcept { return *this / length(); } | |||
/** Returns true if the vector is practically equal to the origin. */ | |||
bool lengthIsBelowEpsilon() const noexcept | |||
{ | |||
const Type epsilon (std::numeric_limits<Type>::epsilon()); | |||
return ! (x < -epsilon || x > epsilon || y < -epsilon || y > epsilon || z < -epsilon || z > epsilon); | |||
} | |||
Type x, y, z; | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,293 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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_OPENGL_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 | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | |||
#include "juce_opengl.h" | |||
//============================================================================== | |||
#if JUCE_IOS | |||
#import <QuartzCore/QuartzCore.h> | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#include <windowsx.h> | |||
#if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
#pragma comment(lib, "OpenGL32.Lib") | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
/* Got an include error here? | |||
If you want to install OpenGL support, the packages to get are "mesa-common-dev" | |||
and "freeglut3-dev". | |||
*/ | |||
#include <GL/glx.h> | |||
//============================================================================== | |||
#elif JUCE_MAC | |||
#include <OpenGL/CGLCurrent.h> // These are both just needed with the 10.5 SDK | |||
#include <OpenGL/OpenGL.h> | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#ifndef GL_GLEXT_PROTOTYPES | |||
#define GL_GLEXT_PROTOTYPES 1 | |||
#endif | |||
#if JUCE_ANDROID_GL_ES_VERSION_3_0 | |||
#include <GLES3/gl3.h> | |||
// workaround for a bug in SDK 18 and 19 | |||
// see: https://stackoverflow.com/questions/31003863/gles-3-0-including-gl2ext-h | |||
#define __gl2_h_ | |||
#include <GLES2/gl2ext.h> | |||
#else | |||
#include <GLES2/gl2.h> | |||
#endif | |||
#endif | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
void OpenGLExtensionFunctions::initialise() | |||
{ | |||
#if JUCE_WINDOWS || JUCE_LINUX | |||
#define JUCE_INIT_GL_FUNCTION(name, returnType, params, callparams) \ | |||
name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); | |||
JUCE_GL_BASE_FUNCTIONS (JUCE_INIT_GL_FUNCTION) | |||
#undef JUCE_INIT_GL_FUNCTION | |||
#define JUCE_INIT_GL_FUNCTION(name, returnType, params, callparams) \ | |||
name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); \ | |||
if (name == nullptr) \ | |||
name = (type_ ## name) OpenGLHelpers::getExtensionFunction (JUCE_STRINGIFY (name ## EXT)); | |||
JUCE_GL_EXTENSION_FUNCTIONS (JUCE_INIT_GL_FUNCTION) | |||
#if JUCE_OPENGL3 | |||
JUCE_GL_VERTEXBUFFER_FUNCTIONS (JUCE_INIT_GL_FUNCTION) | |||
#endif | |||
#undef JUCE_INIT_GL_FUNCTION | |||
#endif | |||
} | |||
#if JUCE_OPENGL_ES | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) \ | |||
returnType OpenGLExtensionFunctions::name params noexcept { return ::name callparams; } | |||
JUCE_GL_BASE_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
JUCE_GL_EXTENSION_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#if JUCE_OPENGL3 | |||
JUCE_GL_VERTEXBUFFER_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#endif | |||
#undef JUCE_DECLARE_GL_FUNCTION | |||
#endif | |||
#undef JUCE_GL_EXTENSION_FUNCTIONS | |||
#if JUCE_DEBUG && ! defined (JUCE_CHECK_OPENGL_ERROR) | |||
static const char* getGLErrorMessage (const GLenum e) noexcept | |||
{ | |||
switch (e) | |||
{ | |||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; | |||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; | |||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; | |||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; | |||
#ifdef GL_STACK_OVERFLOW | |||
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; | |||
#endif | |||
#ifdef GL_STACK_UNDERFLOW | |||
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; | |||
#endif | |||
#ifdef GL_INVALID_FRAMEBUFFER_OPERATION | |||
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; | |||
#endif | |||
default: break; | |||
} | |||
return "Unknown error"; | |||
} | |||
#if JUCE_MAC || JUCE_IOS | |||
#ifndef JUCE_IOS_MAC_VIEW | |||
#if JUCE_IOS | |||
#define JUCE_IOS_MAC_VIEW UIView | |||
#define JUCE_IOS_MAC_WINDOW UIWindow | |||
#else | |||
#define JUCE_IOS_MAC_VIEW NSView | |||
#define JUCE_IOS_MAC_WINDOW NSWindow | |||
#endif | |||
#endif | |||
#endif | |||
static bool checkPeerIsValid (OpenGLContext* context) | |||
{ | |||
jassert (context != nullptr); | |||
if (context != nullptr) | |||
{ | |||
if (auto* comp = context->getTargetComponent()) | |||
{ | |||
if (auto* peer = comp->getPeer()) | |||
{ | |||
#if JUCE_MAC || JUCE_IOS | |||
if (auto* nsView = (JUCE_IOS_MAC_VIEW*) peer->getNativeHandle()) | |||
{ | |||
if (auto* nsWindow = [nsView window]) | |||
{ | |||
#if JUCE_MAC | |||
return ([nsWindow isVisible] | |||
&& (! [nsWindow hidesOnDeactivate] || [NSApp isActive])); | |||
#else | |||
ignoreUnused (nsWindow); | |||
return true; | |||
#endif | |||
} | |||
} | |||
#else | |||
ignoreUnused (peer); | |||
return true; | |||
#endif | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
static void checkGLError (const char* file, const int line) | |||
{ | |||
for (;;) | |||
{ | |||
const GLenum e = glGetError(); | |||
if (e == GL_NO_ERROR) | |||
break; | |||
// if the peer is not valid then ignore errors | |||
if (! checkPeerIsValid (OpenGLContext::getCurrentContext())) | |||
continue; | |||
DBG ("***** " << getGLErrorMessage (e) << " at " << file << " : " << line); | |||
jassertfalse; | |||
} | |||
} | |||
#define JUCE_CHECK_OPENGL_ERROR checkGLError (__FILE__, __LINE__); | |||
#else | |||
#define JUCE_CHECK_OPENGL_ERROR ; | |||
#endif | |||
static void clearGLError() noexcept | |||
{ | |||
while (glGetError() != GL_NO_ERROR) {} | |||
} | |||
struct OpenGLTargetSaver | |||
{ | |||
OpenGLTargetSaver (const OpenGLContext& c) noexcept | |||
: context (c), oldFramebuffer (OpenGLFrameBuffer::getCurrentFrameBufferTarget()) | |||
{ | |||
glGetIntegerv (GL_VIEWPORT, oldViewport); | |||
} | |||
~OpenGLTargetSaver() noexcept | |||
{ | |||
context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, oldFramebuffer); | |||
glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); | |||
} | |||
private: | |||
const OpenGLContext& context; | |||
GLuint oldFramebuffer; | |||
GLint oldViewport[4]; | |||
OpenGLTargetSaver& operator= (const OpenGLTargetSaver&); | |||
}; | |||
} // namespace juce | |||
//============================================================================== | |||
#include "opengl/juce_OpenGLFrameBuffer.cpp" | |||
#include "opengl/juce_OpenGLGraphicsContext.cpp" | |||
#include "opengl/juce_OpenGLHelpers.cpp" | |||
#include "opengl/juce_OpenGLImage.cpp" | |||
#include "opengl/juce_OpenGLPixelFormat.cpp" | |||
#include "opengl/juce_OpenGLShaderProgram.cpp" | |||
#include "opengl/juce_OpenGLTexture.cpp" | |||
//============================================================================== | |||
#if JUCE_MAC || JUCE_IOS | |||
#if JUCE_CLANG | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wundeclared-selector" | |||
#endif | |||
#if JUCE_MAC | |||
#include "native/juce_OpenGL_osx.h" | |||
#else | |||
#include "native/juce_OpenGL_ios.h" | |||
#endif | |||
#if JUCE_CLANG | |||
#pragma clang diagnostic pop | |||
#endif | |||
#elif JUCE_WINDOWS | |||
#include "native/juce_OpenGL_win32.h" | |||
#elif JUCE_LINUX | |||
#include "native/juce_OpenGL_linux_X11.h" | |||
#elif JUCE_ANDROID | |||
#include "native/juce_OpenGL_android.h" | |||
#endif | |||
#include "opengl/juce_OpenGLContext.cpp" | |||
#include "utils/juce_OpenGLAppComponent.cpp" |
@@ -0,0 +1,187 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_opengl | |||
vendor: juce | |||
version: 5.1.2 | |||
name: JUCE OpenGL classes | |||
description: Classes for rendering OpenGL in a JUCE window. | |||
website: http://www.juce.com/juce | |||
license: GPL/Commercial | |||
dependencies: juce_gui_extra | |||
OSXFrameworks: OpenGL | |||
iOSFrameworks: OpenGLES | |||
linuxLibs: GL | |||
mingwLibs: opengl32 | |||
END_JUCE_MODULE_DECLARATION | |||
*******************************************************************************/ | |||
#pragma once | |||
#define JUCE_OPENGL_H_INCLUDED | |||
#include <juce_gui_extra/juce_gui_extra.h> | |||
#undef JUCE_OPENGL | |||
#define JUCE_OPENGL 1 | |||
#if JUCE_IOS || JUCE_ANDROID | |||
#define JUCE_OPENGL_ES 1 | |||
#endif | |||
#if JUCE_WINDOWS | |||
#ifndef APIENTRY | |||
#define APIENTRY __stdcall | |||
#define CLEAR_TEMP_APIENTRY 1 | |||
#endif | |||
#ifndef WINGDIAPI | |||
#define WINGDIAPI __declspec(dllimport) | |||
#define CLEAR_TEMP_WINGDIAPI 1 | |||
#endif | |||
#if JUCE_MINGW | |||
#include <GL/gl.h> | |||
#else | |||
#include <gl/GL.h> | |||
#endif | |||
#ifdef CLEAR_TEMP_WINGDIAPI | |||
#undef WINGDIAPI | |||
#undef CLEAR_TEMP_WINGDIAPI | |||
#endif | |||
#ifdef CLEAR_TEMP_APIENTRY | |||
#undef APIENTRY | |||
#undef CLEAR_TEMP_APIENTRY | |||
#endif | |||
#elif JUCE_LINUX | |||
#include <GL/gl.h> | |||
#undef KeyPress | |||
#elif JUCE_IOS | |||
#if defined (__IPHONE_7_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 | |||
#include <OpenGLES/ES3/gl.h> | |||
#else | |||
#include <OpenGLES/ES2/gl.h> | |||
#endif | |||
#elif JUCE_MAC | |||
#if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7) | |||
#define JUCE_OPENGL3 1 | |||
#include <OpenGL/gl3.h> | |||
#include <OpenGL/gl3ext.h> | |||
#else | |||
#include <OpenGL/gl.h> | |||
#include <OpenGL/glext.h> | |||
#endif | |||
#elif JUCE_ANDROID | |||
#include <android/native_window.h> | |||
#include <android/native_window_jni.h> | |||
#if JUCE_ANDROID_GL_ES_VERSION_3_0 | |||
#define JUCE_OPENGL3 1 | |||
#include <GLES3/gl3.h> | |||
#else | |||
#include <GLES2/gl2.h> | |||
#endif | |||
#include <EGL/egl.h> | |||
#endif | |||
#if GL_ES_VERSION_3_0 | |||
#define JUCE_OPENGL3 1 | |||
#endif | |||
//============================================================================== | |||
/** This macro is a helper for use in GLSL shader code which needs to compile on both OpenGL 2.1 and OpenGL 3.0. | |||
It's mandatory in OpenGL 3.0 to specify the GLSL version. | |||
*/ | |||
#if JUCE_OPENGL3 | |||
#if JUCE_OPENGL_ES | |||
#define JUCE_GLSL_VERSION "#version 300 es" | |||
#else | |||
#define JUCE_GLSL_VERSION "#version 150" | |||
#endif | |||
#else | |||
#define JUCE_GLSL_VERSION "" | |||
#endif | |||
//============================================================================== | |||
#if JUCE_OPENGL_ES || defined (DOXYGEN) | |||
/** This macro is a helper for use in GLSL shader code which needs to compile on both GLES and desktop GL. | |||
Since it's mandatory in GLES to mark a variable with a precision, but the keywords don't exist in normal GLSL, | |||
these macros define the various precision keywords only on GLES. | |||
*/ | |||
#define JUCE_MEDIUMP "mediump" | |||
/** This macro is a helper for use in GLSL shader code which needs to compile on both GLES and desktop GL. | |||
Since it's mandatory in GLES to mark a variable with a precision, but the keywords don't exist in normal GLSL, | |||
these macros define the various precision keywords only on GLES. | |||
*/ | |||
#define JUCE_HIGHP "highp" | |||
/** This macro is a helper for use in GLSL shader code which needs to compile on both GLES and desktop GL. | |||
Since it's mandatory in GLES to mark a variable with a precision, but the keywords don't exist in normal GLSL, | |||
these macros define the various precision keywords only on GLES. | |||
*/ | |||
#define JUCE_LOWP "lowp" | |||
#else | |||
#define JUCE_MEDIUMP | |||
#define JUCE_HIGHP | |||
#define JUCE_LOWP | |||
#endif | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
class OpenGLTexture; | |||
class OpenGLFrameBuffer; | |||
class OpenGLShaderProgram; | |||
} | |||
#include "geometry/juce_Vector3D.h" | |||
#include "geometry/juce_Matrix3D.h" | |||
#include "geometry/juce_Quaternion.h" | |||
#include "geometry/juce_Draggable3DOrientation.h" | |||
#include "native/juce_MissingGLDefinitions.h" | |||
#include "opengl/juce_OpenGLHelpers.h" | |||
#include "opengl/juce_OpenGLPixelFormat.h" | |||
#include "native/juce_OpenGLExtensions.h" | |||
#include "opengl/juce_OpenGLRenderer.h" | |||
#include "opengl/juce_OpenGLContext.h" | |||
#include "opengl/juce_OpenGLFrameBuffer.h" | |||
#include "opengl/juce_OpenGLGraphicsContext.h" | |||
#include "opengl/juce_OpenGLImage.h" | |||
#include "opengl/juce_OpenGLShaderProgram.h" | |||
#include "opengl/juce_OpenGLTexture.h" | |||
#include "utils/juce_OpenGLAppComponent.h" |
@@ -0,0 +1,27 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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_opengl.cpp" |
@@ -0,0 +1,163 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
/** These are important openGL values that aren't defined by default | |||
by the GL headers on various platforms. | |||
*/ | |||
enum MissingOpenGLDefinitions | |||
{ | |||
#ifndef GL_CLAMP_TO_EDGE | |||
GL_CLAMP_TO_EDGE = 0x812f, | |||
#endif | |||
#ifndef GL_NUM_EXTENSIONS | |||
GL_NUM_EXTENSIONS = 0x821d, | |||
#endif | |||
#ifndef GL_BGRA_EXT | |||
GL_BGRA_EXT = 0x80e1, | |||
#endif | |||
#ifndef GL_DEPTH24_STENCIL8 | |||
GL_DEPTH24_STENCIL8 = 0x88F0, | |||
#endif | |||
#ifndef GL_RGBA8 | |||
GL_RGBA8 = GL_RGBA, | |||
#endif | |||
#ifndef GL_RGBA32F | |||
GL_RGBA32F = 0x8814, | |||
#endif | |||
#ifndef GL_COLOR_ATTACHMENT0 | |||
GL_COLOR_ATTACHMENT0 = 0x8CE0, | |||
#endif | |||
#ifndef GL_DEPTH_ATTACHMENT | |||
GL_DEPTH_ATTACHMENT = 0x8D00, | |||
#endif | |||
#ifndef GL_FRAMEBUFFER | |||
GL_FRAMEBUFFER = 0x8D40, | |||
#endif | |||
#ifndef GL_FRAMEBUFFER_BINDING | |||
GL_FRAMEBUFFER_BINDING = 0x8CA6, | |||
#endif | |||
#ifndef GL_FRAMEBUFFER_COMPLETE | |||
GL_FRAMEBUFFER_COMPLETE = 0x8CD5, | |||
#endif | |||
#ifndef GL_RENDERBUFFER | |||
GL_RENDERBUFFER = 0x8D41, | |||
#endif | |||
#ifndef GL_RENDERBUFFER_DEPTH_SIZE | |||
GL_RENDERBUFFER_DEPTH_SIZE = 0x8D54, | |||
#endif | |||
#ifndef GL_STENCIL_ATTACHMENT | |||
GL_STENCIL_ATTACHMENT = 0x8D20, | |||
#endif | |||
#ifndef GL_MULTISAMPLE | |||
GL_MULTISAMPLE = 0x809D, | |||
#endif | |||
#if JUCE_WINDOWS && ! defined (GL_TEXTURE0) | |||
GL_OPERAND0_RGB = 0x8590, | |||
GL_OPERAND1_RGB = 0x8591, | |||
GL_OPERAND0_ALPHA = 0x8598, | |||
GL_OPERAND1_ALPHA = 0x8599, | |||
GL_SRC0_RGB = 0x8580, | |||
GL_SRC1_RGB = 0x8581, | |||
GL_SRC0_ALPHA = 0x8588, | |||
GL_SRC1_ALPHA = 0x8589, | |||
GL_TEXTURE0 = 0x84C0, | |||
GL_TEXTURE1 = 0x84C1, | |||
GL_TEXTURE2 = 0x84C2, | |||
GL_COMBINE = 0x8570, | |||
GL_COMBINE_RGB = 0x8571, | |||
GL_COMBINE_ALPHA = 0x8572, | |||
GL_PREVIOUS = 0x8578, | |||
GL_COMPILE_STATUS = 0x8B81, | |||
GL_LINK_STATUS = 0x8B82, | |||
GL_SHADING_LANGUAGE_VERSION = 0x8B8C, | |||
GL_FRAGMENT_SHADER = 0x8B30, | |||
GL_VERTEX_SHADER = 0x8B31, | |||
GL_ARRAY_BUFFER = 0x8892, | |||
GL_ELEMENT_ARRAY_BUFFER = 0x8893, | |||
GL_STATIC_DRAW = 0x88E4, | |||
GL_DYNAMIC_DRAW = 0x88E8, | |||
GL_STREAM_DRAW = 0x88E0, | |||
WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000, | |||
WGL_DRAW_TO_WINDOW_ARB = 0x2001, | |||
WGL_ACCELERATION_ARB = 0x2003, | |||
WGL_SWAP_METHOD_ARB = 0x2007, | |||
WGL_SUPPORT_OPENGL_ARB = 0x2010, | |||
WGL_PIXEL_TYPE_ARB = 0x2013, | |||
WGL_DOUBLE_BUFFER_ARB = 0x2011, | |||
WGL_COLOR_BITS_ARB = 0x2014, | |||
WGL_RED_BITS_ARB = 0x2015, | |||
WGL_GREEN_BITS_ARB = 0x2017, | |||
WGL_BLUE_BITS_ARB = 0x2019, | |||
WGL_ALPHA_BITS_ARB = 0x201B, | |||
WGL_DEPTH_BITS_ARB = 0x2022, | |||
WGL_STENCIL_BITS_ARB = 0x2023, | |||
WGL_FULL_ACCELERATION_ARB = 0x2027, | |||
WGL_ACCUM_RED_BITS_ARB = 0x201E, | |||
WGL_ACCUM_GREEN_BITS_ARB = 0x201F, | |||
WGL_ACCUM_BLUE_BITS_ARB = 0x2020, | |||
WGL_ACCUM_ALPHA_BITS_ARB = 0x2021, | |||
WGL_STEREO_ARB = 0x2012, | |||
WGL_SAMPLE_BUFFERS_ARB = 0x2041, | |||
WGL_SAMPLES_ARB = 0x2042, | |||
WGL_TYPE_RGBA_ARB = 0x202B, | |||
WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091, | |||
WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092, | |||
WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126, | |||
#endif | |||
#if JUCE_ANDROID | |||
JUCE_RGBA_FORMAT = GL_RGBA | |||
#else | |||
JUCE_RGBA_FORMAT = GL_BGRA_EXT | |||
#endif | |||
}; | |||
#if JUCE_WINDOWS | |||
typedef char GLchar; | |||
typedef pointer_sized_int GLsizeiptr; | |||
typedef pointer_sized_int GLintptr; | |||
#endif | |||
} // namespace juce |
@@ -0,0 +1,158 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux. | |||
@see OpenGLExtensionFunctions | |||
*/ | |||
#define JUCE_GL_BASE_FUNCTIONS(USE_FUNCTION) \ | |||
USE_FUNCTION (glActiveTexture, void, (GLenum p1), (p1))\ | |||
USE_FUNCTION (glBindBuffer, void, (GLenum p1, GLuint p2), (p1, p2))\ | |||
USE_FUNCTION (glDeleteBuffers, void, (GLsizei p1, const GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glGenBuffers, void, (GLsizei p1, GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glBufferData, void, (GLenum p1, GLsizeiptr p2, const GLvoid* p3, GLenum p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glBufferSubData, void, (GLenum p1, GLintptr p2, GLsizeiptr p3, const GLvoid* p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glCreateProgram, GLuint, (), ())\ | |||
USE_FUNCTION (glDeleteProgram, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glCreateShader, GLuint, (GLenum p1), (p1))\ | |||
USE_FUNCTION (glDeleteShader, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glShaderSource, void, (GLuint p1, GLsizei p2, const GLchar** p3, const GLint* p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glCompileShader, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glAttachShader, void, (GLuint p1, GLuint p2), (p1, p2))\ | |||
USE_FUNCTION (glLinkProgram, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glUseProgram, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glGetShaderiv, void, (GLuint p1, GLenum p2, GLint* p3), (p1, p2, p3))\ | |||
USE_FUNCTION (glGetShaderInfoLog, void, (GLuint p1, GLsizei p2, GLsizei* p3, GLchar* p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glGetProgramInfoLog, void, (GLuint p1, GLsizei p2, GLsizei* p3, GLchar* p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glGetProgramiv, void, (GLuint p1, GLenum p2, GLint* p3), (p1, p2, p3))\ | |||
USE_FUNCTION (glGetUniformLocation, GLint, (GLuint p1, const GLchar* p2), (p1, p2))\ | |||
USE_FUNCTION (glGetAttribLocation, GLint, (GLuint p1, const GLchar* p2), (p1, p2))\ | |||
USE_FUNCTION (glVertexAttribPointer, void, (GLuint p1, GLint p2, GLenum p3, GLboolean p4, GLsizei p5, const GLvoid* p6), (p1, p2, p3, p4, p5, p6))\ | |||
USE_FUNCTION (glEnableVertexAttribArray, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glDisableVertexAttribArray, void, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glUniform1f, void, (GLint p1, GLfloat p2), (p1, p2))\ | |||
USE_FUNCTION (glUniform1i, void, (GLint p1, GLint p2), (p1, p2))\ | |||
USE_FUNCTION (glUniform2f, void, (GLint p1, GLfloat p2, GLfloat p3), (p1, p2, p3))\ | |||
USE_FUNCTION (glUniform3f, void, (GLint p1, GLfloat p2, GLfloat p3, GLfloat p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glUniform4f, void, (GLint p1, GLfloat p2, GLfloat p3, GLfloat p4, GLfloat p5), (p1, p2, p3, p4, p5))\ | |||
USE_FUNCTION (glUniform4i, void, (GLint p1, GLint p2, GLint p3, GLint p4, GLint p5), (p1, p2, p3, p4, p5))\ | |||
USE_FUNCTION (glUniform1fv, void, (GLint p1, GLsizei p2, const GLfloat* p3), (p1, p2, p3))\ | |||
USE_FUNCTION (glUniformMatrix2fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glUniformMatrix3fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glUniformMatrix4fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4)) | |||
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux. | |||
@see OpenGLExtensionFunctions | |||
*/ | |||
#define JUCE_GL_EXTENSION_FUNCTIONS(USE_FUNCTION) \ | |||
USE_FUNCTION (glIsRenderbuffer, GLboolean, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glBindRenderbuffer, void, (GLenum p1, GLuint p2), (p1, p2))\ | |||
USE_FUNCTION (glDeleteRenderbuffers, void, (GLsizei p1, const GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glGenRenderbuffers, void, (GLsizei p1, GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glRenderbufferStorage, void, (GLenum p1, GLenum p2, GLsizei p3, GLsizei p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glGetRenderbufferParameteriv, void, (GLenum p1, GLenum p2, GLint* p3), (p1, p2, p3))\ | |||
USE_FUNCTION (glIsFramebuffer, GLboolean, (GLuint p1), (p1))\ | |||
USE_FUNCTION (glBindFramebuffer, void, (GLenum p1, GLuint p2), (p1, p2))\ | |||
USE_FUNCTION (glDeleteFramebuffers, void, (GLsizei p1, const GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glGenFramebuffers, void, (GLsizei p1, GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glCheckFramebufferStatus, GLenum, (GLenum p1), (p1))\ | |||
USE_FUNCTION (glFramebufferTexture2D, void, (GLenum p1, GLenum p2, GLenum p3, GLuint p4, GLint p5), (p1, p2, p3, p4, p5))\ | |||
USE_FUNCTION (glFramebufferRenderbuffer, void, (GLenum p1, GLenum p2, GLenum p3, GLuint p4), (p1, p2, p3, p4))\ | |||
USE_FUNCTION (glGetFramebufferAttachmentParameteriv, void, (GLenum p1, GLenum p2, GLenum p3, GLint* p4), (p1, p2, p3, p4)) | |||
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux. | |||
@see OpenGLExtensionFunctions | |||
*/ | |||
#define JUCE_GL_VERTEXBUFFER_FUNCTIONS(USE_FUNCTION) \ | |||
USE_FUNCTION (glGenVertexArrays, void, (GLsizei p1, GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glDeleteVertexArrays, void, (GLsizei p1, const GLuint* p2), (p1, p2))\ | |||
USE_FUNCTION (glBindVertexArray, void, (GLuint p1), (p1)) | |||
/** This class contains a generated list of OpenGL extension functions, which are either dynamically loaded | |||
for a specific GL context, or simply call-through to the appropriate OS function where available. | |||
*/ | |||
struct OpenGLExtensionFunctions | |||
{ | |||
void initialise(); | |||
#if JUCE_WINDOWS && ! DOXYGEN | |||
typedef char GLchar; | |||
typedef pointer_sized_int GLsizeiptr; | |||
typedef pointer_sized_int GLintptr; | |||
#endif | |||
//============================================================================== | |||
#if JUCE_WINDOWS | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) typedef returnType (__stdcall *type_ ## name) params; type_ ## name name; | |||
JUCE_GL_BASE_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
JUCE_GL_EXTENSION_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#if JUCE_OPENGL3 | |||
JUCE_GL_VERTEXBUFFER_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) typedef returnType (*type_ ## name) params; type_ ## name name; | |||
JUCE_GL_BASE_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
JUCE_GL_EXTENSION_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#if JUCE_OPENGL3 | |||
JUCE_GL_VERTEXBUFFER_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_OPENGL_ES | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) static returnType name params noexcept; | |||
JUCE_GL_BASE_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
JUCE_GL_EXTENSION_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
JUCE_GL_VERTEXBUFFER_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
//============================================================================== | |||
#else | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) inline static returnType name params noexcept { return ::name callparams; } | |||
JUCE_GL_BASE_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#ifndef GL3_PROTOTYPES | |||
#undef JUCE_DECLARE_GL_FUNCTION | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) inline static returnType name params noexcept { return ::name ## EXT callparams; } | |||
#endif | |||
JUCE_GL_EXTENSION_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#if JUCE_OPENGL3 | |||
#ifndef GL3_PROTOTYPES | |||
#undef JUCE_DECLARE_GL_FUNCTION | |||
#define JUCE_DECLARE_GL_FUNCTION(name, returnType, params, callparams) inline static returnType name params noexcept { return ::name ## APPLE callparams; } | |||
#endif | |||
JUCE_GL_VERTEXBUFFER_FUNCTIONS (JUCE_DECLARE_GL_FUNCTION) | |||
#endif | |||
#endif | |||
#undef JUCE_DECLARE_GL_FUNCTION | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,312 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \ | |||
METHOD (layout, "layout", "(IIII)V" ) \ | |||
METHOD (getNativeSurface, "getNativeSurface", "()Landroid/view/Surface;") \ | |||
DECLARE_JNI_CLASS (NativeSurfaceView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView") | |||
#undef JNI_CLASS_MEMBERS | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (addView, "addView", "(Landroid/view/View;)V") \ | |||
METHOD (removeView, "removeView", "(Landroid/view/View;)V") \ | |||
DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup") | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
class OpenGLContext::NativeContext | |||
{ | |||
public: | |||
NativeContext (Component& comp, | |||
const OpenGLPixelFormat& /*pixelFormat*/, | |||
void* /*contextToShareWith*/, | |||
bool /*useMultisampling*/, | |||
OpenGLVersion) | |||
: component (comp), | |||
hasInitialised (false), | |||
juceContext (nullptr), surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT) | |||
{ | |||
JNIEnv* env = getEnv(); | |||
// Do we have a native peer that we can attach to? | |||
if (component.getPeer()->getNativeHandle() == nullptr) | |||
return; | |||
// Initialise the EGL display | |||
if (! initEGLDisplay()) | |||
return; | |||
// create a native surface view | |||
surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(), | |||
JuceAppActivity.createNativeSurfaceView, | |||
reinterpret_cast<jlong> (this))); | |||
if (surfaceView.get() == nullptr) | |||
return; | |||
// add the view to the view hierarchy | |||
// after this the nativecontext can receive callbacks | |||
env->CallVoidMethod ((jobject) component.getPeer()->getNativeHandle(), | |||
AndroidViewGroup.addView, surfaceView.get()); | |||
// initialise the geometry of the view | |||
Rectangle<int> bounds = component.getTopLevelComponent() | |||
->getLocalArea (&component, component.getLocalBounds()); | |||
bounds *= component.getDesktopScaleFactor(); | |||
updateWindowPosition (bounds); | |||
hasInitialised = true; | |||
} | |||
~NativeContext() | |||
{ | |||
JNIEnv* env = getEnv(); | |||
if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getParent)) | |||
env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get()); | |||
} | |||
//============================================================================== | |||
void initialiseOnRenderThread (OpenGLContext& aContext) | |||
{ | |||
jassert (hasInitialised); | |||
// has the context already attached? | |||
jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT); | |||
JNIEnv* env = getEnv(); | |||
// get a pointer to the native window | |||
ANativeWindow* window = nullptr; | |||
if (jobject jSurface = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getNativeSurface)) | |||
window = ANativeWindow_fromSurface (env, jSurface); | |||
jassert (window != nullptr); | |||
// create the surface | |||
surface = eglCreateWindowSurface(display, config, window, 0); | |||
jassert (surface != EGL_NO_SURFACE); | |||
ANativeWindow_release (window); | |||
// create the OpenGL context | |||
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | |||
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); | |||
jassert (context != EGL_NO_CONTEXT); | |||
juceContext = &aContext; | |||
} | |||
void shutdownOnRenderThread() | |||
{ | |||
jassert (hasInitialised); | |||
// is there a context available to detach? | |||
jassert (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT); | |||
eglDestroyContext (display, context); | |||
context = EGL_NO_CONTEXT; | |||
eglDestroySurface (display, surface); | |||
surface = EGL_NO_SURFACE; | |||
} | |||
//============================================================================== | |||
bool makeActive() const noexcept | |||
{ | |||
if (! hasInitialised) | |||
return false; | |||
if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT) | |||
return false; | |||
if (! eglMakeCurrent (display, surface, surface, context)) | |||
return false; | |||
return true; | |||
} | |||
bool isActive() const noexcept { return eglGetCurrentContext() == context; } | |||
static void deactivateCurrentContext() | |||
{ | |||
eglMakeCurrent (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | |||
} | |||
//============================================================================== | |||
void swapBuffers() const noexcept { eglSwapBuffers (display, surface); } | |||
bool setSwapInterval (const int) { return false; } | |||
int getSwapInterval() const { return 0; } | |||
//============================================================================== | |||
bool createdOk() const noexcept { return hasInitialised; } | |||
void* getRawContext() const noexcept { return surfaceView.get(); } | |||
GLuint getFrameBufferID() const noexcept { return 0; } | |||
//============================================================================== | |||
void updateWindowPosition (Rectangle<int> bounds) | |||
{ | |||
if (lastBounds != bounds) | |||
{ | |||
JNIEnv* env = getEnv(); | |||
lastBounds = bounds; | |||
Rectangle<int> r = bounds * Desktop::getInstance().getDisplays().getMainDisplay().scale; | |||
env->CallVoidMethod (surfaceView.get(), NativeSurfaceView.layout, | |||
(jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom()); | |||
} | |||
} | |||
//============================================================================== | |||
// Android Surface Callbacks: | |||
void dispatchDraw (jobject canvas) | |||
{ | |||
ignoreUnused (canvas); | |||
if (juceContext != nullptr) | |||
juceContext->triggerRepaint(); | |||
} | |||
void surfaceChanged (jobject holder, int format, int width, int height) | |||
{ | |||
ignoreUnused (holder, format, width, height); | |||
} | |||
void surfaceCreated (jobject holder); | |||
void surfaceDestroyed (jobject holder); | |||
//============================================================================== | |||
struct Locker { Locker (NativeContext&) {} }; | |||
Component& component; | |||
private: | |||
//============================================================================== | |||
bool initEGLDisplay() | |||
{ | |||
// already initialised? | |||
if (display != EGL_NO_DISPLAY) | |||
return true; | |||
const EGLint attribs[] = | |||
{ | |||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |||
EGL_BLUE_SIZE, 8, | |||
EGL_GREEN_SIZE, 8, | |||
EGL_RED_SIZE, 8, | |||
EGL_ALPHA_SIZE, 0, | |||
EGL_DEPTH_SIZE, 16, | |||
EGL_NONE | |||
}; | |||
EGLint numConfigs; | |||
if ((display = eglGetDisplay (EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) | |||
{ | |||
jassertfalse; | |||
return false; | |||
} | |||
if (! eglInitialize (display, 0, 0)) | |||
{ | |||
jassertfalse; | |||
return false; | |||
} | |||
if (! eglChooseConfig (display, attribs, &config, 1, &numConfigs)) | |||
{ | |||
eglTerminate (display); | |||
jassertfalse; | |||
return false; | |||
} | |||
return true; | |||
} | |||
//============================================================================== | |||
bool hasInitialised; | |||
GlobalRef surfaceView; | |||
Rectangle<int> lastBounds; | |||
OpenGLContext* juceContext; | |||
EGLSurface surface; | |||
EGLContext context; | |||
static EGLDisplay display; | |||
static EGLConfig config; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) | |||
}; | |||
//============================================================================== | |||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), dispatchDrawNative, | |||
void, (JNIEnv* env, jobject nativeView, jlong host, jobject canvas)) | |||
{ | |||
ignoreUnused (nativeView); | |||
setEnv (env); | |||
reinterpret_cast<OpenGLContext::NativeContext*> (host)->dispatchDraw (canvas); | |||
} | |||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceChangedNative, | |||
void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder, jint format, jint width, jint height)) | |||
{ | |||
ignoreUnused (nativeView); | |||
setEnv (env); | |||
reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceChanged (holder, format, width, height); | |||
} | |||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceCreatedNative, | |||
void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder)) | |||
{ | |||
ignoreUnused (nativeView); | |||
setEnv (env); | |||
reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceCreated (holder); | |||
} | |||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceDestroyedNative, | |||
void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder)) | |||
{ | |||
ignoreUnused (nativeView); | |||
setEnv (env); | |||
reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceDestroyed (holder); | |||
} | |||
//============================================================================== | |||
bool OpenGLHelpers::isContextActive() | |||
{ | |||
return eglGetCurrentContext() != EGL_NO_CONTEXT; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,311 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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. | |||
============================================================================== | |||
*/ | |||
@interface JuceGLView : UIView | |||
{ | |||
} | |||
+ (Class) layerClass; | |||
@end | |||
@implementation JuceGLView | |||
+ (Class) layerClass | |||
{ | |||
return [CAEAGLLayer class]; | |||
} | |||
@end | |||
extern "C" GLvoid glResolveMultisampleFramebufferAPPLE(); | |||
namespace juce | |||
{ | |||
class OpenGLContext::NativeContext | |||
{ | |||
public: | |||
NativeContext (Component& c, | |||
const OpenGLPixelFormat& pixFormat, | |||
void* contextToShare, | |||
bool multisampling, | |||
OpenGLVersion version) | |||
: component (c), openGLversion (version), | |||
useDepthBuffer (pixFormat.depthBufferBits > 0), | |||
useMSAA (multisampling) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
if (auto* peer = component.getPeer()) | |||
{ | |||
auto bounds = peer->getAreaCoveredBy (component); | |||
view = [[JuceGLView alloc] initWithFrame: convertToCGRect (bounds)]; | |||
view.opaque = YES; | |||
view.hidden = NO; | |||
view.backgroundColor = [UIColor blackColor]; | |||
view.userInteractionEnabled = NO; | |||
glLayer = (CAEAGLLayer*) [view layer]; | |||
glLayer.opaque = true; | |||
updateWindowPosition (bounds); | |||
[((UIView*) peer->getNativeHandle()) addSubview: view]; | |||
if (version == openGL3_2 && [[UIDevice currentDevice].systemVersion floatValue] >= 7.0) | |||
{ | |||
if (! createContext (kEAGLRenderingAPIOpenGLES3, contextToShare)) | |||
{ | |||
releaseContext(); | |||
createContext (kEAGLRenderingAPIOpenGLES2, contextToShare); | |||
} | |||
} | |||
else | |||
{ | |||
createContext (kEAGLRenderingAPIOpenGLES2, contextToShare); | |||
} | |||
if (context != nil) | |||
{ | |||
// I'd prefer to put this stuff in the initialiseOnRenderThread() call, but doing | |||
// so causes myserious timing-related failures. | |||
[EAGLContext setCurrentContext: context]; | |||
createGLBuffers(); | |||
deactivateCurrentContext(); | |||
} | |||
else | |||
{ | |||
jassertfalse; | |||
} | |||
} | |||
else | |||
{ | |||
jassertfalse; | |||
} | |||
} | |||
} | |||
~NativeContext() | |||
{ | |||
releaseContext(); | |||
[view removeFromSuperview]; | |||
[view release]; | |||
} | |||
void initialiseOnRenderThread (OpenGLContext&) {} | |||
void shutdownOnRenderThread() | |||
{ | |||
JUCE_CHECK_OPENGL_ERROR | |||
freeGLBuffers(); | |||
deactivateCurrentContext(); | |||
} | |||
bool createdOk() const noexcept { return getRawContext() != nullptr; } | |||
void* getRawContext() const noexcept { return context; } | |||
GLuint getFrameBufferID() const noexcept { return useMSAA ? msaaBufferHandle : frameBufferHandle; } | |||
bool makeActive() const noexcept | |||
{ | |||
if (! [EAGLContext setCurrentContext: context]) | |||
return false; | |||
glBindFramebuffer (GL_FRAMEBUFFER, useMSAA ? msaaBufferHandle | |||
: frameBufferHandle); | |||
return true; | |||
} | |||
bool isActive() const noexcept | |||
{ | |||
return [EAGLContext currentContext] == context; | |||
} | |||
static void deactivateCurrentContext() | |||
{ | |||
[EAGLContext setCurrentContext: nil]; | |||
} | |||
void swapBuffers() | |||
{ | |||
if (useMSAA) | |||
{ | |||
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, frameBufferHandle); | |||
glBindFramebuffer (GL_READ_FRAMEBUFFER, msaaBufferHandle); | |||
if (openGLversion >= openGL3_2) | |||
{ | |||
glBlitFramebuffer (0, 0, lastBounds.getWidth(), lastBounds.getHeight(), | |||
0, 0, lastBounds.getWidth(), lastBounds.getHeight(), | |||
GL_COLOR_BUFFER_BIT, GL_NEAREST); | |||
} | |||
else | |||
{ | |||
glResolveMultisampleFramebufferAPPLE(); | |||
} | |||
} | |||
glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle); | |||
[context presentRenderbuffer: GL_RENDERBUFFER]; | |||
if (needToRebuildBuffers) | |||
{ | |||
needToRebuildBuffers = false; | |||
freeGLBuffers(); | |||
createGLBuffers(); | |||
makeActive(); | |||
} | |||
} | |||
void updateWindowPosition (Rectangle<int> bounds) | |||
{ | |||
view.frame = convertToCGRect (bounds); | |||
glLayer.contentsScale = (CGFloat) (Desktop::getInstance().getDisplays().getMainDisplay().scale | |||
/ component.getDesktopScaleFactor()); | |||
if (lastBounds != bounds) | |||
{ | |||
lastBounds = bounds; | |||
needToRebuildBuffers = true; | |||
} | |||
} | |||
bool setSwapInterval (int numFramesPerSwap) noexcept | |||
{ | |||
swapFrames = numFramesPerSwap; | |||
return false; | |||
} | |||
int getSwapInterval() const noexcept { return swapFrames; } | |||
struct Locker { Locker (NativeContext&) {} }; | |||
private: | |||
Component& component; | |||
JuceGLView* view = nil; | |||
CAEAGLLayer* glLayer = nil; | |||
EAGLContext* context = nil; | |||
const OpenGLVersion openGLversion; | |||
const bool useDepthBuffer, useMSAA; | |||
GLuint frameBufferHandle = 0, colorBufferHandle = 0, depthBufferHandle = 0, | |||
msaaColorHandle = 0, msaaBufferHandle = 0; | |||
Rectangle<int> lastBounds; | |||
int swapFrames = 0; | |||
bool needToRebuildBuffers = false; | |||
bool createContext (EAGLRenderingAPI type, void* contextToShare) | |||
{ | |||
jassert (context == nil); | |||
context = [EAGLContext alloc]; | |||
context = contextToShare != nullptr | |||
? [context initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]] | |||
: [context initWithAPI: type]; | |||
return context != nil; | |||
} | |||
void releaseContext() | |||
{ | |||
[context release]; | |||
context = nil; | |||
} | |||
//============================================================================== | |||
void createGLBuffers() | |||
{ | |||
glGenFramebuffers (1, &frameBufferHandle); | |||
glGenRenderbuffers (1, &colorBufferHandle); | |||
glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle); | |||
glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle); | |||
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle); | |||
bool ok = [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer]; | |||
jassert (ok); ignoreUnused (ok); | |||
GLint width, height; | |||
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); | |||
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); | |||
if (useMSAA) | |||
{ | |||
glGenFramebuffers (1, &msaaBufferHandle); | |||
glGenRenderbuffers (1, &msaaColorHandle); | |||
glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle); | |||
glBindRenderbuffer (GL_RENDERBUFFER, msaaColorHandle); | |||
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_RGBA8, width, height); | |||
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorHandle); | |||
} | |||
if (useDepthBuffer) | |||
{ | |||
glGenRenderbuffers (1, &depthBufferHandle); | |||
glBindRenderbuffer (GL_RENDERBUFFER, depthBufferHandle); | |||
if (useMSAA) | |||
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height); | |||
else | |||
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); | |||
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferHandle); | |||
} | |||
jassert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
void freeGLBuffers() | |||
{ | |||
JUCE_CHECK_OPENGL_ERROR | |||
[context renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil]; | |||
deleteFrameBuffer (frameBufferHandle); | |||
deleteFrameBuffer (msaaBufferHandle); | |||
deleteRenderBuffer (colorBufferHandle); | |||
deleteRenderBuffer (depthBufferHandle); | |||
deleteRenderBuffer (msaaColorHandle); | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
static void deleteFrameBuffer (GLuint& i) { if (i != 0) glDeleteFramebuffers (1, &i); i = 0; } | |||
static void deleteRenderBuffer (GLuint& i) { if (i != 0) glDeleteRenderbuffers (1, &i); i = 0; } | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) | |||
}; | |||
//============================================================================== | |||
bool OpenGLHelpers::isContextActive() | |||
{ | |||
return [EAGLContext currentContext] != nil; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,256 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
extern XContext windowHandleXContext; | |||
//============================================================================== | |||
// Defined juce_linux_Windowing.cpp | |||
Rectangle<int> juce_LinuxScaledToPhysicalBounds (ComponentPeer*, Rectangle<int>); | |||
void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy); | |||
void juce_LinuxRemoveRepaintListener (ComponentPeer*, Component* dummy); | |||
//============================================================================== | |||
class OpenGLContext::NativeContext | |||
{ | |||
private: | |||
struct DummyComponent : public Component | |||
{ | |||
DummyComponent (OpenGLContext::NativeContext& nativeParentContext) | |||
: native (nativeParentContext) | |||
{ | |||
} | |||
void handleCommandMessage (int commandId) override | |||
{ | |||
if (commandId == 0) | |||
native.triggerRepaint(); | |||
} | |||
OpenGLContext::NativeContext& native; | |||
}; | |||
public: | |||
NativeContext (Component& comp, | |||
const OpenGLPixelFormat& cPixelFormat, | |||
void* shareContext, | |||
bool /*useMultisampling*/, | |||
OpenGLVersion) | |||
: component (comp), contextToShareWith (shareContext), dummy (*this) | |||
{ | |||
display = XWindowSystem::getInstance()->displayRef(); | |||
ScopedXLock xlock (display); | |||
XSync (display, False); | |||
GLint attribs[] = | |||
{ | |||
GLX_RGBA, | |||
GLX_DOUBLEBUFFER, | |||
GLX_RED_SIZE, cPixelFormat.redBits, | |||
GLX_GREEN_SIZE, cPixelFormat.greenBits, | |||
GLX_BLUE_SIZE, cPixelFormat.blueBits, | |||
GLX_ALPHA_SIZE, cPixelFormat.alphaBits, | |||
GLX_DEPTH_SIZE, cPixelFormat.depthBufferBits, | |||
GLX_STENCIL_SIZE, cPixelFormat.stencilBufferBits, | |||
GLX_ACCUM_RED_SIZE, cPixelFormat.accumulationBufferRedBits, | |||
GLX_ACCUM_GREEN_SIZE, cPixelFormat.accumulationBufferGreenBits, | |||
GLX_ACCUM_BLUE_SIZE, cPixelFormat.accumulationBufferBlueBits, | |||
GLX_ACCUM_ALPHA_SIZE, cPixelFormat.accumulationBufferAlphaBits, | |||
None | |||
}; | |||
bestVisual = glXChooseVisual (display, DefaultScreen (display), attribs); | |||
if (bestVisual == nullptr) | |||
return; | |||
auto* peer = component.getPeer(); | |||
jassert (peer != nullptr); | |||
auto windowH = (Window) peer->getNativeHandle(); | |||
auto colourMap = XCreateColormap (display, windowH, bestVisual->visual, AllocNone); | |||
XSetWindowAttributes swa; | |||
swa.colormap = colourMap; | |||
swa.border_pixel = 0; | |||
swa.event_mask = ExposureMask | StructureNotifyMask; | |||
auto glBounds = component.getTopLevelComponent() | |||
->getLocalArea (&component, component.getLocalBounds()); | |||
glBounds = juce_LinuxScaledToPhysicalBounds (peer, glBounds); | |||
embeddedWindow = XCreateWindow (display, windowH, | |||
glBounds.getX(), glBounds.getY(), | |||
(unsigned int) jmax (1, glBounds.getWidth()), | |||
(unsigned int) jmax (1, glBounds.getHeight()), | |||
0, bestVisual->depth, | |||
InputOutput, | |||
bestVisual->visual, | |||
CWBorderPixel | CWColormap | CWEventMask, | |||
&swa); | |||
XSaveContext (display, (XID) embeddedWindow, windowHandleXContext, (XPointer) peer); | |||
XMapWindow (display, embeddedWindow); | |||
XFreeColormap (display, colourMap); | |||
XSync (display, False); | |||
juce_LinuxAddRepaintListener (peer, &dummy); | |||
} | |||
~NativeContext() | |||
{ | |||
juce_LinuxRemoveRepaintListener (component.getPeer(), &dummy); | |||
if (embeddedWindow != 0) | |||
{ | |||
ScopedXLock xlock (display); | |||
XUnmapWindow (display, embeddedWindow); | |||
XDestroyWindow (display, embeddedWindow); | |||
} | |||
if (bestVisual != nullptr) | |||
XFree (bestVisual); | |||
XWindowSystem::getInstance()->displayUnref(); | |||
} | |||
void initialiseOnRenderThread (OpenGLContext& c) | |||
{ | |||
ScopedXLock xlock (display); | |||
renderContext = glXCreateContext (display, bestVisual, (GLXContext) contextToShareWith, GL_TRUE); | |||
c.makeActive(); | |||
context = &c; | |||
} | |||
void shutdownOnRenderThread() | |||
{ | |||
context = nullptr; | |||
deactivateCurrentContext(); | |||
glXDestroyContext (display, renderContext); | |||
renderContext = nullptr; | |||
} | |||
bool makeActive() const noexcept | |||
{ | |||
return renderContext != 0 | |||
&& glXMakeCurrent (display, embeddedWindow, renderContext); | |||
} | |||
bool isActive() const noexcept | |||
{ | |||
return glXGetCurrentContext() == renderContext && renderContext != 0; | |||
} | |||
static void deactivateCurrentContext() | |||
{ | |||
ScopedXDisplay xDisplay; | |||
glXMakeCurrent (xDisplay.display, None, 0); | |||
} | |||
void swapBuffers() | |||
{ | |||
glXSwapBuffers (display, embeddedWindow); | |||
} | |||
void updateWindowPosition (Rectangle<int> newBounds) | |||
{ | |||
bounds = newBounds; | |||
auto physicalBounds = juce_LinuxScaledToPhysicalBounds (component.getPeer(), bounds); | |||
ScopedXLock xlock (display); | |||
XMoveResizeWindow (display, embeddedWindow, | |||
physicalBounds.getX(), physicalBounds.getY(), | |||
(unsigned int) jmax (1, physicalBounds.getWidth()), | |||
(unsigned int) jmax (1, physicalBounds.getHeight())); | |||
} | |||
bool setSwapInterval (int numFramesPerSwap) | |||
{ | |||
if (numFramesPerSwap == swapFrames) | |||
return true; | |||
if (auto GLXSwapIntervalSGI | |||
= (PFNGLXSWAPINTERVALSGIPROC) OpenGLHelpers::getExtensionFunction ("glXSwapIntervalSGI")) | |||
{ | |||
swapFrames = numFramesPerSwap; | |||
GLXSwapIntervalSGI (numFramesPerSwap); | |||
return true; | |||
} | |||
return false; | |||
} | |||
int getSwapInterval() const { return swapFrames; } | |||
bool createdOk() const noexcept { return true; } | |||
void* getRawContext() const noexcept { return renderContext; } | |||
GLuint getFrameBufferID() const noexcept { return 0; } | |||
void triggerRepaint() | |||
{ | |||
if (context != nullptr) | |||
context->triggerRepaint(); | |||
} | |||
struct Locker { Locker (NativeContext&) {} }; | |||
private: | |||
Component& component; | |||
GLXContext renderContext = {}; | |||
Window embeddedWindow = {}; | |||
int swapFrames = 0; | |||
Rectangle<int> bounds; | |||
XVisualInfo* bestVisual = {}; | |||
void* contextToShareWith; | |||
OpenGLContext* context = {}; | |||
DummyComponent dummy; | |||
::Display* display = {}; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) | |||
}; | |||
//============================================================================== | |||
bool OpenGLHelpers::isContextActive() | |||
{ | |||
ScopedXDisplay xDisplay; | |||
if (xDisplay.display) | |||
{ | |||
ScopedXLock xlock (xDisplay.display); | |||
return glXGetCurrentContext() != 0; | |||
} | |||
return false; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,262 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
class OpenGLContext::NativeContext | |||
{ | |||
public: | |||
NativeContext (Component& component, | |||
const OpenGLPixelFormat& pixFormat, | |||
void* contextToShare, | |||
bool shouldUseMultisampling, | |||
OpenGLVersion version) | |||
: lastSwapTime (0), minSwapTimeMs (0), underrunCounter (0) | |||
{ | |||
NSOpenGLPixelFormatAttribute attribs[64] = { 0 }; | |||
createAttribs (attribs, version, pixFormat, shouldUseMultisampling); | |||
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; | |||
static MouseForwardingNSOpenGLViewClass cls; | |||
view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) | |||
pixelFormat: format]; | |||
#if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) | |||
if ([view respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)]) | |||
[view setWantsBestResolutionOpenGLSurface: YES]; | |||
#endif | |||
[[NSNotificationCenter defaultCenter] addObserver: view | |||
selector: @selector (_surfaceNeedsUpdate:) | |||
name: NSViewGlobalFrameDidChangeNotification | |||
object: view]; | |||
renderContext = [[[NSOpenGLContext alloc] initWithFormat: format | |||
shareContext: (NSOpenGLContext*) contextToShare] autorelease]; | |||
[view setOpenGLContext: renderContext]; | |||
[format release]; | |||
viewAttachment = NSViewComponent::attachViewToComponent (component, view); | |||
} | |||
~NativeContext() | |||
{ | |||
[[NSNotificationCenter defaultCenter] removeObserver: view]; | |||
[renderContext clearDrawable]; | |||
[renderContext setView: nil]; | |||
[view setOpenGLContext: nil]; | |||
renderContext = nil; | |||
} | |||
static void createAttribs (NSOpenGLPixelFormatAttribute* attribs, OpenGLVersion version, | |||
const OpenGLPixelFormat& pixFormat, bool shouldUseMultisampling) | |||
{ | |||
ignoreUnused (version); | |||
int numAttribs = 0; | |||
#if JUCE_OPENGL3 | |||
attribs [numAttribs++] = NSOpenGLPFAOpenGLProfile; | |||
attribs [numAttribs++] = version >= openGL3_2 ? NSOpenGLProfileVersion3_2Core | |||
: NSOpenGLProfileVersionLegacy; | |||
#endif | |||
attribs [numAttribs++] = NSOpenGLPFADoubleBuffer; | |||
attribs [numAttribs++] = NSOpenGLPFAClosestPolicy; | |||
attribs [numAttribs++] = NSOpenGLPFANoRecovery; | |||
attribs [numAttribs++] = NSOpenGLPFAColorSize; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) (pixFormat.redBits + pixFormat.greenBits + pixFormat.blueBits); | |||
attribs [numAttribs++] = NSOpenGLPFAAlphaSize; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.alphaBits; | |||
attribs [numAttribs++] = NSOpenGLPFADepthSize; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.depthBufferBits; | |||
attribs [numAttribs++] = NSOpenGLPFAStencilSize; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.stencilBufferBits; | |||
attribs [numAttribs++] = NSOpenGLPFAAccumSize; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) (pixFormat.accumulationBufferRedBits + pixFormat.accumulationBufferGreenBits | |||
+ pixFormat.accumulationBufferBlueBits + pixFormat.accumulationBufferAlphaBits); | |||
if (shouldUseMultisampling) | |||
{ | |||
attribs [numAttribs++] = NSOpenGLPFAMultisample; | |||
attribs [numAttribs++] = NSOpenGLPFASampleBuffers; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) 1; | |||
attribs [numAttribs++] = NSOpenGLPFASamples; | |||
attribs [numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.multisamplingLevel; | |||
} | |||
} | |||
void initialiseOnRenderThread (OpenGLContext&) {} | |||
void shutdownOnRenderThread() { deactivateCurrentContext(); } | |||
bool createdOk() const noexcept { return getRawContext() != nullptr; } | |||
void* getRawContext() const noexcept { return static_cast<void*> (renderContext); } | |||
GLuint getFrameBufferID() const noexcept { return 0; } | |||
bool makeActive() const noexcept | |||
{ | |||
jassert (renderContext != nil); | |||
if ([renderContext view] != view) | |||
[renderContext setView: view]; | |||
if (NSOpenGLContext* context = [view openGLContext]) | |||
{ | |||
[context makeCurrentContext]; | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool isActive() const noexcept | |||
{ | |||
return [NSOpenGLContext currentContext] == renderContext; | |||
} | |||
static void deactivateCurrentContext() | |||
{ | |||
[NSOpenGLContext clearCurrentContext]; | |||
} | |||
struct Locker | |||
{ | |||
Locker (NativeContext& nc) : cglContext ((CGLContextObj) [nc.renderContext CGLContextObj]) | |||
{ | |||
CGLLockContext (cglContext); | |||
} | |||
~Locker() | |||
{ | |||
CGLUnlockContext (cglContext); | |||
} | |||
private: | |||
CGLContextObj cglContext; | |||
}; | |||
void swapBuffers() | |||
{ | |||
double now = Time::getMillisecondCounterHiRes(); | |||
[renderContext flushBuffer]; | |||
if (minSwapTimeMs > 0) | |||
{ | |||
// When our window is entirely occluded by other windows, flushBuffer | |||
// fails to wait for the swap interval, so the render loop spins at full | |||
// speed, burning CPU. This hack detects when things are going too fast | |||
// and sleeps if necessary. | |||
const double swapTime = Time::getMillisecondCounterHiRes() - now; | |||
const int frameTime = (int) (now - lastSwapTime); | |||
if (swapTime < 0.5 && frameTime < minSwapTimeMs - 3) | |||
{ | |||
if (underrunCounter > 3) | |||
{ | |||
Thread::sleep (2 * (minSwapTimeMs - frameTime)); | |||
now = Time::getMillisecondCounterHiRes(); | |||
} | |||
else | |||
++underrunCounter; | |||
} | |||
else | |||
{ | |||
if (underrunCounter > 0) | |||
--underrunCounter; | |||
} | |||
} | |||
lastSwapTime = now; | |||
} | |||
void updateWindowPosition (Rectangle<int>) {} | |||
bool setSwapInterval (int numFramesPerSwap) | |||
{ | |||
minSwapTimeMs = (numFramesPerSwap * 1000) / 60; | |||
[renderContext setValues: (const GLint*) &numFramesPerSwap | |||
forParameter: NSOpenGLCPSwapInterval]; | |||
return true; | |||
} | |||
int getSwapInterval() const | |||
{ | |||
GLint numFrames = 0; | |||
[renderContext getValues: &numFrames | |||
forParameter: NSOpenGLCPSwapInterval]; | |||
return numFrames; | |||
} | |||
NSOpenGLContext* renderContext; | |||
NSOpenGLView* view; | |||
ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment; | |||
double lastSwapTime; | |||
int minSwapTimeMs, underrunCounter; | |||
//============================================================================== | |||
struct MouseForwardingNSOpenGLViewClass : public ObjCClass<NSOpenGLView> | |||
{ | |||
MouseForwardingNSOpenGLViewClass() : ObjCClass<NSOpenGLView> ("JUCEGLView_") | |||
{ | |||
addMethod (@selector (rightMouseDown:), rightMouseDown, "v@:@"); | |||
addMethod (@selector (rightMouseUp:), rightMouseUp, "v@:@"); | |||
addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "v@:@"); | |||
registerClass(); | |||
} | |||
private: | |||
static void rightMouseDown (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseDown: ev]; } | |||
static void rightMouseUp (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseUp: ev]; } | |||
static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } | |||
}; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) | |||
}; | |||
//============================================================================== | |||
bool OpenGLHelpers::isContextActive() | |||
{ | |||
return CGLGetCurrentContext() != 0; | |||
} | |||
//============================================================================== | |||
void componentPeerAboutToChange (Component& comp, bool shouldSuspend) | |||
{ | |||
if (auto* context = OpenGLContext::getContextAttachedTo (comp)) | |||
context->overrideCanBeAttached (shouldSuspend); | |||
for (auto* child : comp.getChildren()) | |||
componentPeerAboutToChange (*child, shouldSuspend); | |||
} | |||
} // namespace juce |
@@ -0,0 +1,273 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* parent); | |||
//============================================================================== | |||
class OpenGLContext::NativeContext | |||
{ | |||
public: | |||
NativeContext (Component& component, | |||
const OpenGLPixelFormat& pixelFormat, | |||
void* contextToShareWith, | |||
bool /*useMultisampling*/, | |||
OpenGLVersion) | |||
{ | |||
dummyComponent = new DummyComponent (*this); | |||
createNativeWindow (component); | |||
PIXELFORMATDESCRIPTOR pfd; | |||
initialisePixelFormatDescriptor (pfd, pixelFormat); | |||
auto pixFormat = ChoosePixelFormat (dc, &pfd); | |||
if (pixFormat != 0) | |||
SetPixelFormat (dc, pixFormat, &pfd); | |||
renderContext = wglCreateContext (dc); | |||
if (renderContext != 0) | |||
{ | |||
makeActive(); | |||
initialiseGLExtensions(); | |||
auto wglFormat = wglChoosePixelFormatExtension (pixelFormat); | |||
deactivateCurrentContext(); | |||
if (wglFormat != pixFormat && wglFormat != 0) | |||
{ | |||
// can't change the pixel format of a window, so need to delete the | |||
// old one and create a new one.. | |||
releaseDC(); | |||
nativeWindow = nullptr; | |||
createNativeWindow (component); | |||
if (SetPixelFormat (dc, wglFormat, &pfd)) | |||
{ | |||
deleteRenderContext(); | |||
renderContext = wglCreateContext (dc); | |||
} | |||
} | |||
if (contextToShareWith != nullptr) | |||
wglShareLists ((HGLRC) contextToShareWith, renderContext); | |||
component.getTopLevelComponent()->repaint(); | |||
component.repaint(); | |||
} | |||
} | |||
~NativeContext() | |||
{ | |||
deleteRenderContext(); | |||
releaseDC(); | |||
} | |||
void initialiseOnRenderThread (OpenGLContext& c) { context = &c; } | |||
void shutdownOnRenderThread() { deactivateCurrentContext(); context = nullptr; } | |||
static void deactivateCurrentContext() { wglMakeCurrent (0, 0); } | |||
bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc, renderContext) != FALSE; } | |||
bool isActive() const noexcept { return wglGetCurrentContext() == renderContext; } | |||
void swapBuffers() const noexcept { SwapBuffers (dc); } | |||
bool setSwapInterval (int numFramesPerSwap) | |||
{ | |||
jassert (isActive()); // this can only be called when the context is active.. | |||
return wglSwapIntervalEXT != nullptr && wglSwapIntervalEXT (numFramesPerSwap) != FALSE; | |||
} | |||
int getSwapInterval() const | |||
{ | |||
jassert (isActive()); // this can only be called when the context is active.. | |||
return wglGetSwapIntervalEXT != nullptr ? wglGetSwapIntervalEXT() : 0; | |||
} | |||
void updateWindowPosition (Rectangle<int> bounds) | |||
{ | |||
if (nativeWindow != nullptr) | |||
SetWindowPos ((HWND) nativeWindow->getNativeHandle(), 0, | |||
bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), | |||
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); | |||
} | |||
bool createdOk() const noexcept { return getRawContext() != nullptr; } | |||
void* getRawContext() const noexcept { return renderContext; } | |||
unsigned int getFrameBufferID() const noexcept { return 0; } | |||
void triggerRepaint() | |||
{ | |||
if (context != nullptr) | |||
context->triggerRepaint(); | |||
} | |||
struct Locker { Locker (NativeContext&) {} }; | |||
private: | |||
struct DummyComponent : public Component | |||
{ | |||
DummyComponent (NativeContext& c) : context (c) {} | |||
// The windowing code will call this when a paint callback happens | |||
void handleCommandMessage (int) override { context.triggerRepaint(); } | |||
NativeContext& context; | |||
}; | |||
ScopedPointer<DummyComponent> dummyComponent; | |||
ScopedPointer<ComponentPeer> nativeWindow; | |||
HGLRC renderContext; | |||
HDC dc; | |||
OpenGLContext* context = {}; | |||
#define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \ | |||
typedef returnType (__stdcall *type_ ## name) params; type_ ## name name; | |||
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglChoosePixelFormatARB, BOOL, (HDC, const int*, const FLOAT*, UINT, int*, UINT*)) | |||
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglSwapIntervalEXT, BOOL, (int)) | |||
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglGetSwapIntervalEXT, int, ()) | |||
#undef JUCE_DECLARE_WGL_EXTENSION_FUNCTION | |||
void initialiseGLExtensions() | |||
{ | |||
#define JUCE_INIT_WGL_FUNCTION(name) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); | |||
JUCE_INIT_WGL_FUNCTION (wglChoosePixelFormatARB); | |||
JUCE_INIT_WGL_FUNCTION (wglSwapIntervalEXT); | |||
JUCE_INIT_WGL_FUNCTION (wglGetSwapIntervalEXT); | |||
#undef JUCE_INIT_WGL_FUNCTION | |||
} | |||
void createNativeWindow (Component& component) | |||
{ | |||
auto* topComp = component.getTopLevelComponent(); | |||
nativeWindow = createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle()); | |||
if (auto* peer = topComp->getPeer()) | |||
updateWindowPosition (peer->getAreaCoveredBy (component)); | |||
nativeWindow->setVisible (true); | |||
dc = GetDC ((HWND) nativeWindow->getNativeHandle()); | |||
} | |||
void deleteRenderContext() | |||
{ | |||
if (renderContext != 0) | |||
{ | |||
wglDeleteContext (renderContext); | |||
renderContext = 0; | |||
} | |||
} | |||
void releaseDC() | |||
{ | |||
ReleaseDC ((HWND) nativeWindow->getNativeHandle(), dc); | |||
} | |||
static void initialisePixelFormatDescriptor (PIXELFORMATDESCRIPTOR& pfd, const OpenGLPixelFormat& pixelFormat) | |||
{ | |||
zerostruct (pfd); | |||
pfd.nSize = sizeof (pfd); | |||
pfd.nVersion = 1; | |||
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; | |||
pfd.iPixelType = PFD_TYPE_RGBA; | |||
pfd.iLayerType = PFD_MAIN_PLANE; | |||
pfd.cColorBits = (BYTE) (pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits); | |||
pfd.cRedBits = (BYTE) pixelFormat.redBits; | |||
pfd.cGreenBits = (BYTE) pixelFormat.greenBits; | |||
pfd.cBlueBits = (BYTE) pixelFormat.blueBits; | |||
pfd.cAlphaBits = (BYTE) pixelFormat.alphaBits; | |||
pfd.cDepthBits = (BYTE) pixelFormat.depthBufferBits; | |||
pfd.cStencilBits = (BYTE) pixelFormat.stencilBufferBits; | |||
pfd.cAccumBits = (BYTE) (pixelFormat.accumulationBufferRedBits + pixelFormat.accumulationBufferGreenBits | |||
+ pixelFormat.accumulationBufferBlueBits + pixelFormat.accumulationBufferAlphaBits); | |||
pfd.cAccumRedBits = (BYTE) pixelFormat.accumulationBufferRedBits; | |||
pfd.cAccumGreenBits = (BYTE) pixelFormat.accumulationBufferGreenBits; | |||
pfd.cAccumBlueBits = (BYTE) pixelFormat.accumulationBufferBlueBits; | |||
pfd.cAccumAlphaBits = (BYTE) pixelFormat.accumulationBufferAlphaBits; | |||
} | |||
int wglChoosePixelFormatExtension (const OpenGLPixelFormat& pixelFormat) const | |||
{ | |||
int format = 0; | |||
if (wglChoosePixelFormatARB != nullptr) | |||
{ | |||
int atts[64]; | |||
int n = 0; | |||
atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE; | |||
atts[n++] = WGL_SUPPORT_OPENGL_ARB; atts[n++] = GL_TRUE; | |||
atts[n++] = WGL_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE; | |||
atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB; | |||
atts[n++] = WGL_ACCELERATION_ARB; | |||
atts[n++] = WGL_FULL_ACCELERATION_ARB; | |||
atts[n++] = WGL_COLOR_BITS_ARB; atts[n++] = pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits; | |||
atts[n++] = WGL_RED_BITS_ARB; atts[n++] = pixelFormat.redBits; | |||
atts[n++] = WGL_GREEN_BITS_ARB; atts[n++] = pixelFormat.greenBits; | |||
atts[n++] = WGL_BLUE_BITS_ARB; atts[n++] = pixelFormat.blueBits; | |||
atts[n++] = WGL_ALPHA_BITS_ARB; atts[n++] = pixelFormat.alphaBits; | |||
atts[n++] = WGL_DEPTH_BITS_ARB; atts[n++] = pixelFormat.depthBufferBits; | |||
atts[n++] = WGL_STENCIL_BITS_ARB; atts[n++] = pixelFormat.stencilBufferBits; | |||
atts[n++] = WGL_ACCUM_RED_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferRedBits; | |||
atts[n++] = WGL_ACCUM_GREEN_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferGreenBits; | |||
atts[n++] = WGL_ACCUM_BLUE_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferBlueBits; | |||
atts[n++] = WGL_ACCUM_ALPHA_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferAlphaBits; | |||
if (pixelFormat.multisamplingLevel > 0 | |||
&& OpenGLHelpers::isExtensionSupported ("GL_ARB_multisample")) | |||
{ | |||
atts[n++] = WGL_SAMPLE_BUFFERS_ARB; | |||
atts[n++] = 1; | |||
atts[n++] = WGL_SAMPLES_ARB; | |||
atts[n++] = pixelFormat.multisamplingLevel; | |||
} | |||
atts[n++] = 0; | |||
jassert (n <= numElementsInArray (atts)); | |||
UINT formatsCount = 0; | |||
wglChoosePixelFormatARB (dc, atts, nullptr, 1, &format, &formatsCount); | |||
} | |||
return format; | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) | |||
}; | |||
//============================================================================== | |||
bool OpenGLHelpers::isContextActive() | |||
{ | |||
return wglGetCurrentContext() != 0; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,348 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Creates an OpenGL context, which can be attached to a component. | |||
To render some OpenGL, you should create an instance of an OpenGLContext, | |||
and call attachTo() to make it use a component as its render target. | |||
To provide threaded rendering, you can supply an OpenGLRenderer object that | |||
will be used to render each frame. | |||
Before your target component or OpenGLRenderer is deleted, you MUST call | |||
detach() or delete the OpenGLContext to allow the background thread to | |||
stop and the native resources to be freed safely. | |||
@see OpenGLRenderer | |||
*/ | |||
class JUCE_API OpenGLContext | |||
{ | |||
public: | |||
OpenGLContext(); | |||
/** Destructor. */ | |||
~OpenGLContext(); | |||
//============================================================================== | |||
/** Gives the context an OpenGLRenderer to use to do the drawing. | |||
The object that you give it will not be owned by the context, so it's the caller's | |||
responsibility to manage its lifetime and make sure that it doesn't get deleted | |||
while the context may be using it. To stop the context using a renderer, just call | |||
this method with a null pointer. | |||
Note: This must be called BEFORE attaching your context to a target component! | |||
*/ | |||
void setRenderer (OpenGLRenderer*) noexcept; | |||
/** Attaches the context to a target component. | |||
If the component is not fully visible, this call will wait until the component | |||
is shown before actually creating a native context for it. | |||
When a native context is created, a thread is started, and will be used to call | |||
the OpenGLRenderer methods. The context will be floated above the target component, | |||
and when the target moves, it will track it. If the component is hidden/shown, the | |||
context may be deleted and re-created. | |||
*/ | |||
void attachTo (Component&); | |||
/** Detaches the context from its target component and deletes any native resources. | |||
If the context has not been attached, this will do nothing. Otherwise, it will block | |||
until the context and its thread have been cleaned up. | |||
*/ | |||
void detach(); | |||
/** Returns true if the context is attached to a component and is on-screen. | |||
Note that if you call attachTo() for a non-visible component, this method will | |||
return false until the component is made visible. | |||
*/ | |||
bool isAttached() const noexcept; | |||
/** Returns the component to which this context is currently attached, or nullptr. */ | |||
Component* getTargetComponent() const noexcept; | |||
/** If the given component has an OpenGLContext attached, then this will return it. */ | |||
static OpenGLContext* getContextAttachedTo (Component& component) noexcept; | |||
//============================================================================== | |||
/** Sets the pixel format which you'd like to use for the target GL surface. | |||
Note: This must be called BEFORE attaching your context to a target component! | |||
*/ | |||
void setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept; | |||
/** Provides a context with which you'd like this context's resources to be shared. | |||
The object passed-in here is a platform-dependent native context object, and | |||
must not be deleted while this context may still be using it! To turn off sharing, | |||
you can call this method with a null pointer. | |||
Note: This must be called BEFORE attaching your context to a target component! | |||
*/ | |||
void setNativeSharedContext (void* nativeContextToShareWith) noexcept; | |||
/** Enables multisampling on platforms where this is implemented. | |||
If enabling this, you must call this method before attachTo(). | |||
*/ | |||
void setMultisamplingEnabled (bool) noexcept; | |||
/** Returns true if shaders can be used in this context. */ | |||
bool areShadersAvailable() const; | |||
/** OpenGL versions, used by setOpenGLVersionRequired(). */ | |||
enum OpenGLVersion | |||
{ | |||
defaultGLVersion = 0, | |||
openGL3_2 | |||
}; | |||
/** Sets a preference for the version of GL that this context should use, if possible. | |||
Some platforms may ignore this value. | |||
*/ | |||
void setOpenGLVersionRequired (OpenGLVersion) noexcept; | |||
/** Enables or disables the use of the GL context to perform 2D rendering | |||
of the component to which it is attached. | |||
If this is false, then only your OpenGLRenderer will be used to perform | |||
any rendering. If true, then each time your target's paint() method needs | |||
to be called, an OpenGLGraphicsContext will be used to render it, (after | |||
calling your OpenGLRenderer if there is one). | |||
By default this is set to true. If you're not using any paint() method functionality | |||
and are doing all your rendering in an OpenGLRenderer, you should disable it | |||
to improve performance. | |||
Note: This must be called BEFORE attaching your context to a target component! | |||
*/ | |||
void setComponentPaintingEnabled (bool shouldPaintComponent) noexcept; | |||
/** Enables or disables continuous repainting. | |||
If set to true, the context will run a loop, re-rendering itself without waiting | |||
for triggerRepaint() to be called, at a frequency determined by the swap interval | |||
(see setSwapInterval). If false, then after each render callback, it will wait for | |||
another call to triggerRepaint() before rendering again. | |||
This is disabled by default. | |||
@see setSwapInterval | |||
*/ | |||
void setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept; | |||
/** Asynchronously causes a repaint to be made. */ | |||
void triggerRepaint(); | |||
//============================================================================== | |||
/** This retrieves an object that was previously stored with setAssociatedObject(). | |||
If no object is found with the given name, this will return nullptr. | |||
This method must only be called from within the GL rendering methods. | |||
@see setAssociatedObject | |||
*/ | |||
ReferenceCountedObject* getAssociatedObject (const char* name) const; | |||
/** Attaches a named object to the context, which will be deleted when the context is | |||
destroyed. | |||
This allows you to store an object which will be released before the context is | |||
deleted. The main purpose is for caching GL objects such as shader programs, which | |||
will become invalid when the context is deleted. | |||
This method must only be called from within the GL rendering methods. | |||
*/ | |||
void setAssociatedObject (const char* name, ReferenceCountedObject* newObject); | |||
//============================================================================== | |||
/** Makes this context the currently active one. | |||
You should never need to call this in normal use - the context will already be | |||
active when OpenGLRenderer::renderOpenGL() is invoked. | |||
*/ | |||
bool makeActive() const noexcept; | |||
/** Returns true if this context is currently active for the calling thread. */ | |||
bool isActive() const noexcept; | |||
/** If any context is active on the current thread, this deactivates it. | |||
Note that on some platforms, like Android, this isn't possible. | |||
*/ | |||
static void deactivateCurrentContext(); | |||
/** Returns the context that's currently in active use by the calling thread, or | |||
nullptr if no context is active. | |||
*/ | |||
static OpenGLContext* getCurrentContext(); | |||
//============================================================================== | |||
/** Swaps the buffers (if the context can do this). | |||
There's normally no need to call this directly - the buffers will be swapped | |||
automatically after your OpenGLRenderer::renderOpenGL() method has been called. | |||
*/ | |||
void swapBuffers(); | |||
/** Sets whether the context checks the vertical sync before swapping. | |||
The value is the number of frames to allow between buffer-swapping. This is | |||
fairly system-dependent, but 0 turns off syncing, 1 makes it swap on frame-boundaries, | |||
and greater numbers indicate that it should swap less often. | |||
By default, this will be set to 1. | |||
Returns true if it sets the value successfully - some platforms won't support | |||
this setting. | |||
@see setContinuousRepainting | |||
*/ | |||
bool setSwapInterval (int numFramesPerSwap); | |||
/** Returns the current swap-sync interval. | |||
See setSwapInterval() for info about the value returned. | |||
*/ | |||
int getSwapInterval() const; | |||
//============================================================================== | |||
/** Execute a lambda, function or functor on the OpenGL thread with an active | |||
context. | |||
This method will attempt to execute functor on the OpenGL thread. If | |||
blockUntilFinished is true then the method will block until the functor | |||
has finished executing. | |||
This function can only be called if the context is attached to a component. | |||
Otherwise, this function will assert. | |||
This function is useful when you need to excute house-keeping tasks such | |||
as allocating, deallocating textures or framebuffers. As such, the functor | |||
will execute without locking the message thread. Therefore, it is not | |||
intended for any drawing commands or GUI code. Any GUI code should be | |||
executed in the OpenGLRenderer::renderOpenGL callback instead. | |||
*/ | |||
template <typename T> | |||
void executeOnGLThread (T&& functor, bool blockUntilFinished); | |||
//============================================================================== | |||
/** Returns the scale factor used by the display that is being rendered. | |||
The scale is that of the display - see Desktop::Displays::Display::scale | |||
Note that this should only be called during an OpenGLRenderer::renderOpenGL() | |||
callback - at other times the value it returns is undefined. | |||
*/ | |||
double getRenderingScale() const noexcept { return currentRenderScale; } | |||
//============================================================================== | |||
/** If this context is backed by a frame buffer, this returns its ID number, | |||
or 0 if the context does not use a framebuffer. | |||
*/ | |||
unsigned int getFrameBufferID() const noexcept; | |||
/** Returns an OS-dependent handle to some kind of underlting OS-provided GL context. | |||
The exact type of the value returned will depend on the OS and may change | |||
if the implementation changes. If you want to use this, digging around in the | |||
native code is probably the best way to find out what it is. | |||
*/ | |||
void* getRawContext() const noexcept; | |||
/** This structure holds a set of dynamically loaded GL functions for use on this context. */ | |||
OpenGLExtensionFunctions extensions; | |||
//============================================================================== | |||
/** Draws the currently selected texture into this context at its original size. | |||
@param targetClipArea the target area to draw into (in top-left origin coords) | |||
@param anchorPosAndTextureSize the position of this rectangle is the texture's top-left | |||
anchor position in the target space, and the size must be | |||
the total size of the texture. | |||
@param contextWidth the width of the context or framebuffer that is being drawn into, | |||
used for scaling of the coordinates. | |||
@param contextHeight the height of the context or framebuffer that is being drawn into, | |||
used for vertical flipping of the y coordinates. | |||
@param textureOriginIsBottomLeft if true, the texture's origin is treated as being at | |||
(0, 0). If false, it is assumed to be (0, 1) | |||
*/ | |||
void copyTexture (const Rectangle<int>& targetClipArea, | |||
const Rectangle<int>& anchorPosAndTextureSize, | |||
int contextWidth, int contextHeight, | |||
bool textureOriginIsBottomLeft); | |||
/** Changes the amount of GPU memory that the internal cache for Images is allowed to use. */ | |||
void setImageCacheSize (size_t cacheSizeBytes) noexcept; | |||
/** Returns the amount of GPU memory that the internal cache for Images is allowed to use. */ | |||
size_t getImageCacheSize() const noexcept; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
class NativeContext; | |||
#endif | |||
private: | |||
class CachedImage; | |||
class Attachment; | |||
NativeContext* nativeContext = nullptr; | |||
OpenGLRenderer* renderer = nullptr; | |||
double currentRenderScale = 1.0; | |||
ScopedPointer<Attachment> attachment; | |||
OpenGLPixelFormat openGLPixelFormat; | |||
void* contextToShareWith = nullptr; | |||
OpenGLVersion versionRequired = defaultGLVersion; | |||
size_t imageCacheMaxSize = 8 * 1024 * 1024; | |||
bool renderComponents = true, useMultisampling = false, continuousRepaint = false, overrideCanAttach = false; | |||
//============================================================================== | |||
struct AsyncWorker : public ReferenceCountedObject | |||
{ | |||
typedef ReferenceCountedObjectPtr<AsyncWorker> Ptr; | |||
virtual void operator() (OpenGLContext&) = 0; | |||
virtual ~AsyncWorker() {} | |||
}; | |||
template <typename FunctionType> | |||
struct AsyncWorkerFunctor : public AsyncWorker | |||
{ | |||
AsyncWorkerFunctor (FunctionType functorToUse) : functor (functorToUse) {} | |||
void operator() (OpenGLContext& callerContext) override { functor (callerContext); } | |||
FunctionType functor; | |||
JUCE_DECLARE_NON_COPYABLE (AsyncWorkerFunctor) | |||
}; | |||
//============================================================================== | |||
friend void componentPeerAboutToChange (Component&, bool); | |||
void overrideCanBeAttached (bool); | |||
//============================================================================== | |||
CachedImage* getCachedImage() const noexcept; | |||
void execute (AsyncWorker::Ptr, bool); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLContext) | |||
}; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
template <typename FunctionType> | |||
void OpenGLContext::executeOnGLThread (FunctionType&& f, bool shouldBlock) { execute (new AsyncWorkerFunctor<FunctionType> (f), shouldBlock); } | |||
#endif | |||
} // namespace juce |
@@ -0,0 +1,354 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
class OpenGLFrameBuffer::Pimpl | |||
{ | |||
public: | |||
Pimpl (OpenGLContext& c, const int w, const int h, | |||
const bool wantsDepthBuffer, const bool wantsStencilBuffer) | |||
: context (c), width (w), height (h), | |||
textureID (0), frameBufferID (0), depthOrStencilBuffer (0), | |||
hasDepthBuffer (false), hasStencilBuffer (false) | |||
{ | |||
// Framebuffer objects can only be created when the current thread has an active OpenGL | |||
// context. You'll need to create this object in one of the OpenGLContext's callbacks. | |||
jassert (OpenGLHelpers::isContextActive()); | |||
#if JUCE_WINDOWS || JUCE_LINUX | |||
if (context.extensions.glGenFramebuffers == nullptr) | |||
return; | |||
#endif | |||
context.extensions.glGenFramebuffers (1, &frameBufferID); | |||
bind(); | |||
glGenTextures (1, &textureID); | |||
glBindTexture (GL_TEXTURE_2D, textureID); | |||
JUCE_CHECK_OPENGL_ERROR | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |||
JUCE_CHECK_OPENGL_ERROR | |||
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | |||
JUCE_CHECK_OPENGL_ERROR | |||
context.extensions.glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0); | |||
if (wantsDepthBuffer || wantsStencilBuffer) | |||
{ | |||
context.extensions.glGenRenderbuffers (1, &depthOrStencilBuffer); | |||
context.extensions.glBindRenderbuffer (GL_RENDERBUFFER, depthOrStencilBuffer); | |||
jassert (context.extensions.glIsRenderbuffer (depthOrStencilBuffer)); | |||
context.extensions.glRenderbufferStorage (GL_RENDERBUFFER, | |||
(wantsDepthBuffer && wantsStencilBuffer) ? GL_DEPTH24_STENCIL8 | |||
#if JUCE_OPENGL_ES | |||
: GL_DEPTH_COMPONENT16, | |||
#else | |||
: GL_DEPTH_COMPONENT, | |||
#endif | |||
width, height); | |||
GLint params = 0; | |||
context.extensions.glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_DEPTH_SIZE, ¶ms); | |||
context.extensions.glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthOrStencilBuffer); | |||
if (wantsStencilBuffer) | |||
context.extensions.glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthOrStencilBuffer); | |||
hasDepthBuffer = wantsDepthBuffer; | |||
hasStencilBuffer = wantsStencilBuffer; | |||
} | |||
unbind(); | |||
} | |||
~Pimpl() | |||
{ | |||
if (OpenGLHelpers::isContextActive()) | |||
{ | |||
if (textureID != 0) | |||
glDeleteTextures (1, &textureID); | |||
if (depthOrStencilBuffer != 0) | |||
context.extensions.glDeleteRenderbuffers (1, &depthOrStencilBuffer); | |||
if (frameBufferID != 0) | |||
context.extensions.glDeleteFramebuffers (1, &frameBufferID); | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
} | |||
bool createdOk() const | |||
{ | |||
return frameBufferID != 0 && textureID != 0; | |||
} | |||
void bind() | |||
{ | |||
context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferID); | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
void unbind() | |||
{ | |||
context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, context.getFrameBufferID()); | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
OpenGLContext& context; | |||
const int width, height; | |||
GLuint textureID, frameBufferID, depthOrStencilBuffer; | |||
bool hasDepthBuffer, hasStencilBuffer; | |||
private: | |||
bool checkStatus() noexcept | |||
{ | |||
const GLenum status = context.extensions.glCheckFramebufferStatus (GL_FRAMEBUFFER); | |||
return status == GL_NO_ERROR | |||
|| status == GL_FRAMEBUFFER_COMPLETE; | |||
} | |||
JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
}; | |||
//============================================================================== | |||
class OpenGLFrameBuffer::SavedState | |||
{ | |||
public: | |||
SavedState (OpenGLFrameBuffer& buffer, const int w, const int h) | |||
: width (w), height (h), | |||
data ((size_t) (w * h)) | |||
{ | |||
buffer.readPixels (data, Rectangle<int> (w, h)); | |||
} | |||
bool restore (OpenGLContext& context, OpenGLFrameBuffer& buffer) | |||
{ | |||
if (buffer.initialise (context, width, height)) | |||
{ | |||
buffer.writePixels (data, Rectangle<int> (width, height)); | |||
return true; | |||
} | |||
return false; | |||
} | |||
private: | |||
const int width, height; | |||
HeapBlock<PixelARGB> data; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState) | |||
}; | |||
//============================================================================== | |||
OpenGLFrameBuffer::OpenGLFrameBuffer() {} | |||
OpenGLFrameBuffer::~OpenGLFrameBuffer() {} | |||
bool OpenGLFrameBuffer::initialise (OpenGLContext& context, int width, int height) | |||
{ | |||
jassert (context.isActive()); // The context must be active when creating a framebuffer! | |||
pimpl = nullptr; | |||
pimpl = new Pimpl (context, width, height, false, false); | |||
if (! pimpl->createdOk()) | |||
pimpl = nullptr; | |||
return pimpl != nullptr; | |||
} | |||
bool OpenGLFrameBuffer::initialise (OpenGLContext& context, const Image& image) | |||
{ | |||
if (! image.isARGB()) | |||
return initialise (context, image.convertedToFormat (Image::ARGB)); | |||
Image::BitmapData bitmap (image, Image::BitmapData::readOnly); | |||
return initialise (context, bitmap.width, bitmap.height) | |||
&& writePixels ((const PixelARGB*) bitmap.data, image.getBounds()); | |||
} | |||
bool OpenGLFrameBuffer::initialise (OpenGLFrameBuffer& other) | |||
{ | |||
const Pimpl* const p = other.pimpl; | |||
if (p == nullptr) | |||
{ | |||
pimpl = nullptr; | |||
return true; | |||
} | |||
const Rectangle<int> area (pimpl->width, pimpl->height); | |||
if (initialise (p->context, area.getWidth(), area.getHeight())) | |||
{ | |||
pimpl->bind(); | |||
#if ! JUCE_ANDROID | |||
glEnable (GL_TEXTURE_2D); | |||
clearGLError(); | |||
#endif | |||
glBindTexture (GL_TEXTURE_2D, p->textureID); | |||
pimpl->context.copyTexture (area, area, area.getWidth(), area.getHeight(), false); | |||
glBindTexture (GL_TEXTURE_2D, 0); | |||
JUCE_CHECK_OPENGL_ERROR | |||
pimpl->unbind(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void OpenGLFrameBuffer::release() | |||
{ | |||
pimpl = nullptr; | |||
savedState = nullptr; | |||
} | |||
void OpenGLFrameBuffer::saveAndRelease() | |||
{ | |||
if (pimpl != nullptr) | |||
{ | |||
savedState = new SavedState (*this, pimpl->width, pimpl->height); | |||
pimpl = nullptr; | |||
} | |||
} | |||
bool OpenGLFrameBuffer::reloadSavedCopy (OpenGLContext& context) | |||
{ | |||
if (savedState != nullptr) | |||
{ | |||
ScopedPointer<SavedState> state (savedState); | |||
if (state->restore (context, *this)) | |||
return true; | |||
savedState = state; | |||
} | |||
return false; | |||
} | |||
int OpenGLFrameBuffer::getWidth() const noexcept { return pimpl != nullptr ? pimpl->width : 0; } | |||
int OpenGLFrameBuffer::getHeight() const noexcept { return pimpl != nullptr ? pimpl->height : 0; } | |||
GLuint OpenGLFrameBuffer::getTextureID() const noexcept { return pimpl != nullptr ? pimpl->textureID : 0; } | |||
bool OpenGLFrameBuffer::makeCurrentRenderingTarget() | |||
{ | |||
// trying to use a framebuffer after saving it with saveAndRelease()! Be sure to call | |||
// reloadSavedCopy() to put it back into GPU memory before using it.. | |||
jassert (savedState == nullptr); | |||
if (pimpl == nullptr) | |||
return false; | |||
pimpl->bind(); | |||
return true; | |||
} | |||
GLuint OpenGLFrameBuffer::getFrameBufferID() const noexcept | |||
{ | |||
return pimpl != nullptr ? pimpl->frameBufferID : 0; | |||
} | |||
GLuint OpenGLFrameBuffer::getCurrentFrameBufferTarget() noexcept | |||
{ | |||
GLint fb; | |||
glGetIntegerv (GL_FRAMEBUFFER_BINDING, &fb); | |||
return (GLuint) fb; | |||
} | |||
void OpenGLFrameBuffer::releaseAsRenderingTarget() | |||
{ | |||
if (pimpl != nullptr) | |||
pimpl->unbind(); | |||
} | |||
void OpenGLFrameBuffer::clear (Colour colour) | |||
{ | |||
if (makeCurrentRenderingTarget()) | |||
{ | |||
OpenGLHelpers::clear (colour); | |||
releaseAsRenderingTarget(); | |||
} | |||
} | |||
void OpenGLFrameBuffer::makeCurrentAndClear() | |||
{ | |||
if (makeCurrentRenderingTarget()) | |||
{ | |||
glClearColor (0, 0, 0, 0); | |||
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | |||
} | |||
} | |||
bool OpenGLFrameBuffer::readPixels (PixelARGB* target, const Rectangle<int>& area) | |||
{ | |||
if (! makeCurrentRenderingTarget()) | |||
return false; | |||
glPixelStorei (GL_PACK_ALIGNMENT, 4); | |||
glReadPixels (area.getX(), area.getY(), area.getWidth(), area.getHeight(), | |||
JUCE_RGBA_FORMAT, GL_UNSIGNED_BYTE, target); | |||
pimpl->unbind(); | |||
return true; | |||
} | |||
bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>& area) | |||
{ | |||
OpenGLTargetSaver ts (pimpl->context); | |||
if (! makeCurrentRenderingTarget()) | |||
return false; | |||
glDisable (GL_DEPTH_TEST); | |||
glDisable (GL_BLEND); | |||
JUCE_CHECK_OPENGL_ERROR | |||
OpenGLTexture tex; | |||
tex.loadARGB (data, area.getWidth(), area.getHeight()); | |||
glViewport (0, 0, pimpl->width, pimpl->height); | |||
pimpl->context.copyTexture (area, Rectangle<int> (area.getX(), area.getY(), | |||
tex.getWidth(), tex.getHeight()), | |||
pimpl->width, pimpl->height, true); | |||
JUCE_CHECK_OPENGL_ERROR | |||
return true; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,134 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Creates an openGL frame buffer. | |||
*/ | |||
class JUCE_API OpenGLFrameBuffer | |||
{ | |||
public: | |||
/** Creates an uninitialised buffer. | |||
To actually allocate the buffer, use initialise(). | |||
*/ | |||
OpenGLFrameBuffer(); | |||
/** Destructor. */ | |||
~OpenGLFrameBuffer(); | |||
//============================================================================== | |||
/** Tries to allocates a buffer of the given size. | |||
Note that a valid openGL context must be selected when you call this method, | |||
or it will fail. | |||
*/ | |||
bool initialise (OpenGLContext& context, int width, int height); | |||
/** Tries to allocates a buffer containing a copy of a given image. | |||
Note that a valid openGL context must be selected when you call this method, | |||
or it will fail. | |||
*/ | |||
bool initialise (OpenGLContext& context, const Image& content); | |||
/** Tries to allocate a copy of another framebuffer. | |||
*/ | |||
bool initialise (OpenGLFrameBuffer& other); | |||
/** Releases the buffer, if one has been allocated. | |||
Any saved state that was created with saveAndRelease() will also be freed by this call. | |||
*/ | |||
void release(); | |||
/** If the framebuffer is active, this will save a stashed copy of its contents in main memory, | |||
and will release the GL buffer. | |||
After saving, the original state can be restored again by calling reloadSavedCopy(). | |||
*/ | |||
void saveAndRelease(); | |||
/** Restores the framebuffer content that was previously saved using saveAndRelease(). | |||
After saving to main memory, the original state can be restored by calling restoreToGPUMemory(). | |||
*/ | |||
bool reloadSavedCopy (OpenGLContext& context); | |||
//============================================================================== | |||
/** Returns true if a valid buffer has been allocated. */ | |||
bool isValid() const noexcept { return pimpl != nullptr; } | |||
/** Returns the width of the buffer. */ | |||
int getWidth() const noexcept; | |||
/** Returns the height of the buffer. */ | |||
int getHeight() const noexcept; | |||
/** Returns the texture ID number for using this buffer as a texture. */ | |||
GLuint getTextureID() const noexcept; | |||
//============================================================================== | |||
/** Selects this buffer as the current OpenGL rendering target. */ | |||
bool makeCurrentRenderingTarget(); | |||
/** Deselects this buffer as the current OpenGL rendering target. */ | |||
void releaseAsRenderingTarget(); | |||
/** Returns the ID of this framebuffer, or 0 if it isn't initialised. */ | |||
GLuint getFrameBufferID() const noexcept; | |||
/** Returns the current frame buffer ID for the current context. */ | |||
static GLuint getCurrentFrameBufferTarget() noexcept; | |||
/** Clears the framebuffer with the specified colour. */ | |||
void clear (Colour colour); | |||
/** Selects the framebuffer as the current target, and clears it to transparent. */ | |||
void makeCurrentAndClear(); | |||
/** Reads an area of pixels from the framebuffer into a 32-bit ARGB pixel array. | |||
The lineStride is measured as a number of pixels, not bytes - pass a stride | |||
of 0 to indicate a packed array. | |||
*/ | |||
bool readPixels (PixelARGB* targetData, const Rectangle<int>& sourceArea); | |||
/** Writes an area of pixels into the framebuffer from a specified pixel array. | |||
The lineStride is measured as a number of pixels, not bytes - pass a stride | |||
of 0 to indicate a packed array. | |||
*/ | |||
bool writePixels (const PixelARGB* srcData, const Rectangle<int>& targetArea); | |||
private: | |||
class Pimpl; | |||
friend struct ContainerDeletePolicy<Pimpl>; | |||
ScopedPointer<Pimpl> pimpl; | |||
class SavedState; | |||
friend struct ContainerDeletePolicy<SavedState>; | |||
ScopedPointer<SavedState> savedState; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLFrameBuffer) | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,96 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
/** Creates a graphics context object that will render into the given OpenGL target. | |||
The caller is responsible for deleting this object when no longer needed. | |||
*/ | |||
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& target, | |||
int width, int height); | |||
/** Creates a graphics context object that will render into the given OpenGL target. | |||
The caller is responsible for deleting this object when no longer needed. | |||
*/ | |||
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, | |||
OpenGLFrameBuffer& target); | |||
/** Creates a graphics context object that will render into the given OpenGL target. | |||
The caller is responsible for deleting this object when no longer needed. | |||
*/ | |||
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, | |||
unsigned int frameBufferID, | |||
int width, int height); | |||
//============================================================================== | |||
/** | |||
Used to create custom shaders for use with an openGL 2D rendering context. | |||
Given a GL-based rendering context, you can write a fragment shader that applies some | |||
kind of per-pixel effect. | |||
*/ | |||
struct JUCE_API OpenGLGraphicsContextCustomShader | |||
{ | |||
/** Creates a custom shader. | |||
The shader code will not be compiled until actually needed, so it's OK to call this | |||
constructor when no GL context is active. | |||
The code should be a normal fragment shader. As well as the usual GLSL variables, there is | |||
also an automatically declared varying vec2 called "pixelPos", which indicates the pixel | |||
position within the graphics context of the pixel being drawn. There is also a varying value | |||
"pixelAlpha", which indicates the alpha by which the pixel should be multiplied, so that the | |||
edges of any clip-region masks are anti-aliased correctly. | |||
*/ | |||
OpenGLGraphicsContextCustomShader (const String& fragmentShaderCode); | |||
/** Destructor. */ | |||
~OpenGLGraphicsContextCustomShader(); | |||
/** Returns the program, if it has been linked and is active. | |||
This can be called when you're about to use fillRect, to set up any uniforms/textures that | |||
the program may require. | |||
*/ | |||
OpenGLShaderProgram* getProgram (LowLevelGraphicsContext&) const; | |||
/** Applies the shader to a rectangle within the graphics context. */ | |||
void fillRect (LowLevelGraphicsContext&, const Rectangle<int>& area) const; | |||
/** Attempts to compile the program if necessary, and returns an error message if it fails. */ | |||
Result checkCompilation (LowLevelGraphicsContext&); | |||
/** Returns the code that was used to create this object. */ | |||
const String& getFragmentShaderCode() const noexcept { return code; } | |||
private: | |||
String code, hashName; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLGraphicsContextCustomShader) | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,135 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
void OpenGLHelpers::resetErrorState() | |||
{ | |||
while (glGetError() != GL_NO_ERROR) {} | |||
} | |||
void* OpenGLHelpers::getExtensionFunction (const char* functionName) | |||
{ | |||
#if JUCE_WINDOWS | |||
return (void*) wglGetProcAddress (functionName); | |||
#elif JUCE_LINUX | |||
return (void*) glXGetProcAddress ((const GLubyte*) functionName); | |||
#else | |||
static void* handle = dlopen (nullptr, RTLD_LAZY); | |||
return dlsym (handle, functionName); | |||
#endif | |||
} | |||
bool OpenGLHelpers::isExtensionSupported (const char* const extensionName) | |||
{ | |||
jassert (extensionName != nullptr); // you must supply a genuine string for this. | |||
jassert (isContextActive()); // An OpenGL context will need to be active before calling this. | |||
const char* extensions = (const char*) glGetString (GL_EXTENSIONS); | |||
jassert (extensions != nullptr); // Perhaps you didn't activate an OpenGL context before calling this? | |||
for (;;) | |||
{ | |||
const char* found = strstr (extensions, extensionName); | |||
if (found == nullptr) | |||
break; | |||
extensions = found + strlen (extensionName); | |||
if (extensions[0] == ' ' || extensions[0] == 0) | |||
return true; | |||
} | |||
return false; | |||
} | |||
void OpenGLHelpers::clear (Colour colour) | |||
{ | |||
glClearColor (colour.getFloatRed(), colour.getFloatGreen(), | |||
colour.getFloatBlue(), colour.getFloatAlpha()); | |||
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | |||
} | |||
void OpenGLHelpers::enableScissorTest (const Rectangle<int>& clip) | |||
{ | |||
glEnable (GL_SCISSOR_TEST); | |||
glScissor (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight()); | |||
} | |||
String OpenGLHelpers::translateVertexShaderToV3 (const String& code) | |||
{ | |||
#if JUCE_OPENGL3 | |||
if (OpenGLShaderProgram::getLanguageVersion() > 1.2) | |||
{ | |||
String output; | |||
#if JUCE_ANDROID | |||
{ | |||
int numAttributes = 0; | |||
for (int p = code.indexOf (0, "attribute "); p >= 0; p = code.indexOf (p + 1, "attribute ")) | |||
numAttributes++; | |||
int last = 0; | |||
for (int p = code.indexOf (0, "attribute "); p >= 0; p = code.indexOf (p + 1, "attribute ")) | |||
{ | |||
output += code.substring (last, p) + String ("layout(location=") + String (--numAttributes) + ") in "; | |||
last = p + 10; | |||
} | |||
output += code.substring (last); | |||
} | |||
#else | |||
output = code.replace ("attribute", "in"); | |||
#endif | |||
return JUCE_GLSL_VERSION "\n" + output.replace ("varying", "out"); | |||
} | |||
#endif | |||
return code; | |||
} | |||
String OpenGLHelpers::translateFragmentShaderToV3 (const String& code) | |||
{ | |||
#if JUCE_OPENGL3 | |||
if (OpenGLShaderProgram::getLanguageVersion() > 1.2) | |||
return JUCE_GLSL_VERSION "\n" | |||
"out " JUCE_MEDIUMP " vec4 fragColor;\n" | |||
+ code.replace ("varying", "in") | |||
.replace ("texture2D", "texture") | |||
.replace ("gl_FragColor", "fragColor"); | |||
#endif | |||
return code; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,73 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
A set of miscellaneous openGL helper functions. | |||
*/ | |||
class JUCE_API OpenGLHelpers | |||
{ | |||
public: | |||
/** Clears the GL error state. */ | |||
static void resetErrorState(); | |||
/** Returns true if the current thread has an active OpenGL context. */ | |||
static bool isContextActive(); | |||
/** Clears the current context using the given colour. */ | |||
static void clear (Colour colour); | |||
static void enableScissorTest (const Rectangle<int>& clip); | |||
/** Checks whether the current context supports the specified extension. */ | |||
static bool isExtensionSupported (const char* extensionName); | |||
/** Returns the address of a named GL extension function */ | |||
static void* getExtensionFunction (const char* functionName); | |||
/** Makes some simple textual changes to a shader program to try to convert old GLSL | |||
keywords to their v3 equivalents. | |||
Before doing this, the function will check whether the current context is actually | |||
using a later version of the language, and if not it will not make any changes. | |||
Obviously this is not a real parser, so will only work on simple code! | |||
*/ | |||
static String translateVertexShaderToV3 (const String&); | |||
/** Makes some simple textual changes to a shader program to try to convert old GLSL | |||
keywords to their v3 equivalents. | |||
Before doing this, the function will check whether the current context is actually | |||
using a later version of the language, and if not it will not make any changes. | |||
Obviously this is not a real parser, so will only work on simple code! | |||
*/ | |||
static String translateFragmentShaderToV3 (const String&); | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,207 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
class OpenGLFrameBufferImage : public ImagePixelData | |||
{ | |||
public: | |||
OpenGLFrameBufferImage (OpenGLContext& c, int w, int h) | |||
: ImagePixelData (Image::ARGB, w, h), | |||
context (c), | |||
pixelStride (4), | |||
lineStride (width * pixelStride) | |||
{ | |||
} | |||
bool initialise() | |||
{ | |||
return frameBuffer.initialise (context, width, height); | |||
} | |||
LowLevelGraphicsContext* createLowLevelContext() override | |||
{ | |||
sendDataChangeMessage(); | |||
return createOpenGLGraphicsContext (context, frameBuffer); | |||
} | |||
ImageType* createType() const override { return new OpenGLImageType(); } | |||
ImagePixelData::Ptr clone() override | |||
{ | |||
Image newImage (new OpenGLFrameBufferImage (context, width, height)); | |||
Graphics g (newImage); | |||
g.drawImageAt (Image (this), 0, 0, false); | |||
return newImage.getPixelData(); | |||
} | |||
void initialiseBitmapData (Image::BitmapData& bitmapData, int x, int y, Image::BitmapData::ReadWriteMode mode) override | |||
{ | |||
bitmapData.pixelFormat = pixelFormat; | |||
bitmapData.lineStride = lineStride; | |||
bitmapData.pixelStride = pixelStride; | |||
switch (mode) | |||
{ | |||
case Image::BitmapData::writeOnly: DataReleaser<Dummy, Writer>::initialise (frameBuffer, bitmapData, x, y); break; | |||
case Image::BitmapData::readOnly: DataReleaser<Reader, Dummy> ::initialise (frameBuffer, bitmapData, x, y); break; | |||
case Image::BitmapData::readWrite: DataReleaser<Reader, Writer>::initialise (frameBuffer, bitmapData, x, y); break; | |||
default: jassertfalse; break; | |||
} | |||
if (mode != Image::BitmapData::readOnly) | |||
sendDataChangeMessage(); | |||
} | |||
OpenGLContext& context; | |||
OpenGLFrameBuffer frameBuffer; | |||
private: | |||
int pixelStride, lineStride; | |||
struct Dummy | |||
{ | |||
Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {} | |||
static void read (OpenGLFrameBuffer&, Image::BitmapData& , int, int) noexcept {} | |||
static void write (const PixelARGB*) noexcept {} | |||
}; | |||
struct Reader | |||
{ | |||
static void read (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y) | |||
{ | |||
frameBuffer.readPixels ((PixelARGB*) bitmapData.data, | |||
Rectangle<int> (x, frameBuffer.getHeight() - (y + bitmapData.height), bitmapData.width, bitmapData.height)); | |||
verticalRowFlip ((PixelARGB*) bitmapData.data, bitmapData.width, bitmapData.height); | |||
} | |||
static void verticalRowFlip (PixelARGB* const data, const int w, const int h) | |||
{ | |||
HeapBlock<PixelARGB> tempRow ((size_t) w); | |||
const size_t rowSize = sizeof (PixelARGB) * (size_t) w; | |||
for (int y = 0; y < h / 2; ++y) | |||
{ | |||
PixelARGB* const row1 = data + y * w; | |||
PixelARGB* const row2 = data + (h - 1 - y) * w; | |||
memcpy (tempRow, row1, rowSize); | |||
memcpy (row1, row2, rowSize); | |||
memcpy (row2, tempRow, rowSize); | |||
} | |||
} | |||
}; | |||
struct Writer | |||
{ | |||
Writer (OpenGLFrameBuffer& fb, int x, int y, int w, int h) noexcept | |||
: frameBuffer (fb), area (x, y, w, h) | |||
{} | |||
void write (const PixelARGB* const data) const noexcept | |||
{ | |||
HeapBlock<PixelARGB> invertedCopy ((size_t) (area.getWidth() * area.getHeight())); | |||
const size_t rowSize = sizeof (PixelARGB) * (size_t) area.getWidth(); | |||
for (int y = 0; y < area.getHeight(); ++y) | |||
memcpy (invertedCopy + area.getWidth() * y, | |||
data + area.getWidth() * (area.getHeight() - 1 - y), rowSize); | |||
frameBuffer.writePixels (invertedCopy, area); | |||
} | |||
OpenGLFrameBuffer& frameBuffer; | |||
const Rectangle<int> area; | |||
JUCE_DECLARE_NON_COPYABLE (Writer) | |||
}; | |||
template <class ReaderType, class WriterType> | |||
struct DataReleaser : public Image::BitmapData::BitmapDataReleaser | |||
{ | |||
DataReleaser (OpenGLFrameBuffer& fb, int x, int y, int w, int h) | |||
: data ((size_t) (w * h)), | |||
writer (fb, x, y, w, h) | |||
{} | |||
~DataReleaser() | |||
{ | |||
writer.write (data); | |||
} | |||
static void initialise (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y) | |||
{ | |||
DataReleaser* r = new DataReleaser (frameBuffer, x, y, bitmapData.width, bitmapData.height); | |||
bitmapData.dataReleaser = r; | |||
bitmapData.data = (uint8*) r->data.get(); | |||
bitmapData.lineStride = (bitmapData.width * bitmapData.pixelStride + 3) & ~3; | |||
ReaderType::read (frameBuffer, bitmapData, x, y); | |||
} | |||
HeapBlock<PixelARGB> data; | |||
WriterType writer; | |||
}; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLFrameBufferImage) | |||
}; | |||
//============================================================================== | |||
OpenGLImageType::OpenGLImageType() {} | |||
OpenGLImageType::~OpenGLImageType() {} | |||
int OpenGLImageType::getTypeID() const | |||
{ | |||
return 3; | |||
} | |||
ImagePixelData::Ptr OpenGLImageType::create (Image::PixelFormat, int width, int height, bool /*shouldClearImage*/) const | |||
{ | |||
OpenGLContext* currentContext = OpenGLContext::getCurrentContext(); | |||
jassert (currentContext != nullptr); // an OpenGL image can only be created when a valid context is active! | |||
ScopedPointer<OpenGLFrameBufferImage> im (new OpenGLFrameBufferImage (*currentContext, width, height)); | |||
if (! im->initialise()) | |||
return ImagePixelData::Ptr(); | |||
im->frameBuffer.clear (Colours::transparentBlack); | |||
return im.release(); | |||
} | |||
OpenGLFrameBuffer* OpenGLImageType::getFrameBufferFrom (const Image& image) | |||
{ | |||
if (OpenGLFrameBufferImage* const glImage = dynamic_cast<OpenGLFrameBufferImage*> (image.getPixelData())) | |||
return &(glImage->frameBuffer); | |||
return nullptr; | |||
} | |||
} // namespace juce |
@@ -0,0 +1,53 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
A type of ImagePixelData that stores its image data in an OpenGL | |||
framebuffer, allowing a JUCE Image object to wrap a framebuffer. | |||
By creating an Image from an instance of an OpenGLFrameBufferImage, | |||
you can then use a Graphics object to draw into the framebuffer using normal | |||
JUCE 2D operations. | |||
@see Image, ImageType, ImagePixelData, OpenGLFrameBuffer | |||
*/ | |||
class JUCE_API OpenGLImageType : public ImageType | |||
{ | |||
public: | |||
OpenGLImageType(); | |||
~OpenGLImageType(); | |||
ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool shouldClearImage) const override; | |||
int getTypeID() const override; | |||
static OpenGLFrameBuffer* getFrameBufferFrom (const Image&); | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,68 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
OpenGLPixelFormat::OpenGLPixelFormat (const int bitsPerRGBComponent, | |||
const int alphaBits_, | |||
const int depthBufferBits_, | |||
const int stencilBufferBits_) noexcept | |||
: redBits (bitsPerRGBComponent), | |||
greenBits (bitsPerRGBComponent), | |||
blueBits (bitsPerRGBComponent), | |||
alphaBits (alphaBits_), | |||
depthBufferBits (depthBufferBits_), | |||
stencilBufferBits (stencilBufferBits_), | |||
accumulationBufferRedBits (0), | |||
accumulationBufferGreenBits (0), | |||
accumulationBufferBlueBits (0), | |||
accumulationBufferAlphaBits (0), | |||
multisamplingLevel (0) | |||
{ | |||
} | |||
bool OpenGLPixelFormat::operator== (const OpenGLPixelFormat& other) const noexcept | |||
{ | |||
return redBits == other.redBits | |||
&& greenBits == other.greenBits | |||
&& blueBits == other.blueBits | |||
&& alphaBits == other.alphaBits | |||
&& depthBufferBits == other.depthBufferBits | |||
&& stencilBufferBits == other.stencilBufferBits | |||
&& accumulationBufferRedBits == other.accumulationBufferRedBits | |||
&& accumulationBufferGreenBits == other.accumulationBufferGreenBits | |||
&& accumulationBufferBlueBits == other.accumulationBufferBlueBits | |||
&& accumulationBufferAlphaBits == other.accumulationBufferAlphaBits | |||
&& multisamplingLevel == other.multisamplingLevel; | |||
} | |||
bool OpenGLPixelFormat::operator!= (const OpenGLPixelFormat& other) const noexcept | |||
{ | |||
return ! operator== (other); | |||
} | |||
} // namespace juce |
@@ -0,0 +1,70 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Represents the various properties of an OpenGL pixel format. | |||
@see OpenGLContext::setPixelFormat | |||
*/ | |||
class JUCE_API OpenGLPixelFormat | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an OpenGLPixelFormat. | |||
The default constructor just initialises the object as a simple 8-bit | |||
RGBA format. | |||
*/ | |||
OpenGLPixelFormat (int bitsPerRGBComponent = 8, | |||
int alphaBits = 8, | |||
int depthBufferBits = 16, | |||
int stencilBufferBits = 0) noexcept; | |||
bool operator== (const OpenGLPixelFormat&) const noexcept; | |||
bool operator!= (const OpenGLPixelFormat&) const noexcept; | |||
//============================================================================== | |||
int redBits; /**< The number of bits per pixel to use for the red channel. */ | |||
int greenBits; /**< The number of bits per pixel to use for the green channel. */ | |||
int blueBits; /**< The number of bits per pixel to use for the blue channel. */ | |||
int alphaBits; /**< The number of bits per pixel to use for the alpha channel. */ | |||
int depthBufferBits; /**< The number of bits per pixel to use for a depth buffer. */ | |||
int stencilBufferBits; /**< The number of bits per pixel to use for a stencil buffer. */ | |||
int accumulationBufferRedBits; /**< The number of bits per pixel to use for an accumulation buffer's red channel. */ | |||
int accumulationBufferGreenBits; /**< The number of bits per pixel to use for an accumulation buffer's green channel. */ | |||
int accumulationBufferBlueBits; /**< The number of bits per pixel to use for an accumulation buffer's blue channel. */ | |||
int accumulationBufferAlphaBits; /**< The number of bits per pixel to use for an accumulation buffer's alpha channel. */ | |||
uint8 multisamplingLevel; /**< The number of samples to use for full-scene multisampled anti-aliasing (if available). */ | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,80 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
A base class that should be implemented by classes which want to render openGL | |||
on a background thread. | |||
@see OpenGLContext | |||
*/ | |||
class JUCE_API OpenGLRenderer | |||
{ | |||
public: | |||
OpenGLRenderer() {} | |||
virtual ~OpenGLRenderer() {} | |||
/** Called when a new GL context has been created. | |||
You can use this as an opportunity to create your textures, shaders, etc. | |||
When the method is invoked, the new GL context will be active. | |||
Note that this callback will be made on a background thread, so make sure | |||
that your implementation is thread-safe. | |||
*/ | |||
virtual void newOpenGLContextCreated() = 0; | |||
/** Called when you should render the next openGL frame. | |||
Note that this callback will be made on a background thread. | |||
If the context is attached to a component in order to do component rendering, | |||
then the MessageManager will be locked when this callback is made. | |||
If no component rendering is being done, then the MessageManager will not be | |||
locked, and you'll need to make sure your code is thread-safe in any | |||
interactions it has with your GUI classes. | |||
For information about how to trigger a render callback, see | |||
OpenGLContext::triggerRepaint() and OpenGLContext::setContinuousRepainting(). | |||
*/ | |||
virtual void renderOpenGL() = 0; | |||
/** Called when the current openGL context is about to close. | |||
You can use this opportunity to release any GL resources that you may have | |||
created. | |||
Note that this callback will be made on a background thread, so make sure | |||
that your implementation is thread-safe. | |||
(Also note that on Android, this callback won't happen, because there's currently | |||
no way to implement it..) | |||
*/ | |||
virtual void openGLContextClosing() = 0; | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,193 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
OpenGLShaderProgram::OpenGLShaderProgram (const OpenGLContext& c) noexcept : context (c) | |||
{ | |||
} | |||
OpenGLShaderProgram::~OpenGLShaderProgram() noexcept | |||
{ | |||
release(); | |||
} | |||
GLuint OpenGLShaderProgram::getProgramID() const noexcept | |||
{ | |||
if (programID == 0) | |||
{ | |||
// This method should only be called when the current thread has an active OpenGL context. | |||
jassert (OpenGLHelpers::isContextActive()); | |||
programID = context.extensions.glCreateProgram(); | |||
} | |||
return programID; | |||
} | |||
void OpenGLShaderProgram::release() noexcept | |||
{ | |||
if (programID != 0) | |||
{ | |||
context.extensions.glDeleteProgram (programID); | |||
programID = 0; | |||
} | |||
} | |||
double OpenGLShaderProgram::getLanguageVersion() | |||
{ | |||
return String::fromUTF8 ((const char*) glGetString (GL_SHADING_LANGUAGE_VERSION)) | |||
.retainCharacters("1234567890.").getDoubleValue(); | |||
} | |||
bool OpenGLShaderProgram::addShader (const String& code, GLenum type) | |||
{ | |||
GLuint shaderID = context.extensions.glCreateShader (type); | |||
const GLchar* c = code.toRawUTF8(); | |||
context.extensions.glShaderSource (shaderID, 1, &c, nullptr); | |||
context.extensions.glCompileShader (shaderID); | |||
GLint status = GL_FALSE; | |||
context.extensions.glGetShaderiv (shaderID, GL_COMPILE_STATUS, &status); | |||
if (status == GL_FALSE) | |||
{ | |||
GLchar infoLog [16384]; | |||
GLsizei infoLogLength = 0; | |||
context.extensions.glGetShaderInfoLog (shaderID, sizeof (infoLog), &infoLogLength, infoLog); | |||
errorLog = String (infoLog, (size_t) infoLogLength); | |||
#if JUCE_DEBUG && ! JUCE_DONT_ASSERT_ON_GLSL_COMPILE_ERROR | |||
// Your GLSL code contained compile errors! | |||
// Hopefully this compile log should help to explain what went wrong. | |||
DBG (errorLog); | |||
jassertfalse; | |||
#endif | |||
return false; | |||
} | |||
context.extensions.glAttachShader (getProgramID(), shaderID); | |||
context.extensions.glDeleteShader (shaderID); | |||
JUCE_CHECK_OPENGL_ERROR | |||
return true; | |||
} | |||
bool OpenGLShaderProgram::addVertexShader (const String& code) { return addShader (code, GL_VERTEX_SHADER); } | |||
bool OpenGLShaderProgram::addFragmentShader (const String& code) { return addShader (code, GL_FRAGMENT_SHADER); } | |||
bool OpenGLShaderProgram::link() noexcept | |||
{ | |||
// This method can only be used when the current thread has an active OpenGL context. | |||
jassert (OpenGLHelpers::isContextActive()); | |||
GLuint progID = getProgramID(); | |||
context.extensions.glLinkProgram (progID); | |||
GLint status = GL_FALSE; | |||
context.extensions.glGetProgramiv (progID, GL_LINK_STATUS, &status); | |||
if (status == GL_FALSE) | |||
{ | |||
GLchar infoLog [16384]; | |||
GLsizei infoLogLength = 0; | |||
context.extensions.glGetProgramInfoLog (progID, sizeof (infoLog), &infoLogLength, infoLog); | |||
errorLog = String (infoLog, (size_t) infoLogLength); | |||
#if JUCE_DEBUG && ! JUCE_DONT_ASSERT_ON_GLSL_COMPILE_ERROR | |||
// Your GLSL code contained link errors! | |||
// Hopefully this compile log should help to explain what went wrong. | |||
DBG (errorLog); | |||
jassertfalse; | |||
#endif | |||
} | |||
JUCE_CHECK_OPENGL_ERROR | |||
return status != GL_FALSE; | |||
} | |||
void OpenGLShaderProgram::use() const noexcept | |||
{ | |||
// The shader program must have been successfully linked when this method is called! | |||
jassert (programID != 0); | |||
context.extensions.glUseProgram (programID); | |||
} | |||
GLint OpenGLShaderProgram::getUniformIDFromName (const char* uniformName) const noexcept | |||
{ | |||
// The shader program must be active when this method is called! | |||
jassert (programID != 0); | |||
return (GLint) context.extensions.glGetUniformLocation (programID, uniformName); | |||
} | |||
void OpenGLShaderProgram::setUniform (const char* name, GLfloat n1) noexcept { context.extensions.glUniform1f (getUniformIDFromName (name), n1); } | |||
void OpenGLShaderProgram::setUniform (const char* name, GLint n1) noexcept { context.extensions.glUniform1i (getUniformIDFromName (name), n1); } | |||
void OpenGLShaderProgram::setUniform (const char* name, GLfloat n1, GLfloat n2) noexcept { context.extensions.glUniform2f (getUniformIDFromName (name), n1, n2); } | |||
void OpenGLShaderProgram::setUniform (const char* name, GLfloat n1, GLfloat n2, GLfloat n3) noexcept { context.extensions.glUniform3f (getUniformIDFromName (name), n1, n2, n3); } | |||
void OpenGLShaderProgram::setUniform (const char* name, GLfloat n1, GLfloat n2, GLfloat n3, float n4) noexcept { context.extensions.glUniform4f (getUniformIDFromName (name), n1, n2, n3, n4); } | |||
void OpenGLShaderProgram::setUniform (const char* name, GLint n1, GLint n2, GLint n3, GLint n4) noexcept { context.extensions.glUniform4i (getUniformIDFromName (name), n1, n2, n3, n4); } | |||
void OpenGLShaderProgram::setUniform (const char* name, const GLfloat* values, GLsizei numValues) noexcept { context.extensions.glUniform1fv (getUniformIDFromName (name), numValues, values); } | |||
void OpenGLShaderProgram::setUniformMat2 (const char* name, const GLfloat* v, GLint num, GLboolean trns) noexcept { context.extensions.glUniformMatrix2fv (getUniformIDFromName (name), num, trns, v); } | |||
void OpenGLShaderProgram::setUniformMat3 (const char* name, const GLfloat* v, GLint num, GLboolean trns) noexcept { context.extensions.glUniformMatrix3fv (getUniformIDFromName (name), num, trns, v); } | |||
void OpenGLShaderProgram::setUniformMat4 (const char* name, const GLfloat* v, GLint num, GLboolean trns) noexcept { context.extensions.glUniformMatrix4fv (getUniformIDFromName (name), num, trns, v); } | |||
//============================================================================== | |||
OpenGLShaderProgram::Attribute::Attribute (const OpenGLShaderProgram& program, const char* name) | |||
: attributeID ((GLuint) program.context.extensions.glGetAttribLocation (program.getProgramID(), name)) | |||
{ | |||
#if JUCE_DEBUG && ! JUCE_DONT_ASSERT_ON_GLSL_COMPILE_ERROR | |||
jassert ((GLint) attributeID >= 0); | |||
#endif | |||
} | |||
//============================================================================== | |||
OpenGLShaderProgram::Uniform::Uniform (const OpenGLShaderProgram& program, const char* const name) | |||
: uniformID (program.context.extensions.glGetUniformLocation (program.getProgramID(), name)), context (program.context) | |||
{ | |||
#if JUCE_DEBUG && ! JUCE_DONT_ASSERT_ON_GLSL_COMPILE_ERROR | |||
jassert (uniformID >= 0); | |||
#endif | |||
} | |||
void OpenGLShaderProgram::Uniform::set (GLfloat n1) const noexcept { context.extensions.glUniform1f (uniformID, n1); } | |||
void OpenGLShaderProgram::Uniform::set (GLint n1) const noexcept { context.extensions.glUniform1i (uniformID, n1); } | |||
void OpenGLShaderProgram::Uniform::set (GLfloat n1, GLfloat n2) const noexcept { context.extensions.glUniform2f (uniformID, n1, n2); } | |||
void OpenGLShaderProgram::Uniform::set (GLfloat n1, GLfloat n2, GLfloat n3) const noexcept { context.extensions.glUniform3f (uniformID, n1, n2, n3); } | |||
void OpenGLShaderProgram::Uniform::set (GLfloat n1, GLfloat n2, GLfloat n3, float n4) const noexcept { context.extensions.glUniform4f (uniformID, n1, n2, n3, n4); } | |||
void OpenGLShaderProgram::Uniform::set (GLint n1, GLint n2, GLint n3, GLint n4) const noexcept { context.extensions.glUniform4i (uniformID, n1, n2, n3, n4); } | |||
void OpenGLShaderProgram::Uniform::set (const GLfloat* values, GLsizei numValues) const noexcept { context.extensions.glUniform1fv (uniformID, numValues, values); } | |||
void OpenGLShaderProgram::Uniform::setMatrix2 (const GLfloat* v, GLint num, GLboolean trns) const noexcept { context.extensions.glUniformMatrix2fv (uniformID, num, trns, v); } | |||
void OpenGLShaderProgram::Uniform::setMatrix3 (const GLfloat* v, GLint num, GLboolean trns) const noexcept { context.extensions.glUniformMatrix3fv (uniformID, num, trns, v); } | |||
void OpenGLShaderProgram::Uniform::setMatrix4 (const GLfloat* v, GLint num, GLboolean trns) const noexcept { context.extensions.glUniformMatrix4fv (uniformID, num, trns, v); } | |||
} // namespace juce |
@@ -0,0 +1,203 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Manages an OpenGL shader program. | |||
*/ | |||
class JUCE_API OpenGLShaderProgram | |||
{ | |||
public: | |||
/** Creates a shader for use in a particular GL context. */ | |||
OpenGLShaderProgram (const OpenGLContext&) noexcept; | |||
/** Destructor. */ | |||
~OpenGLShaderProgram() noexcept; | |||
/** Returns the version of GLSL that the current context supports. | |||
E.g. | |||
@code | |||
if (OpenGLShaderProgram::getLanguageVersion() > 1.199) | |||
{ | |||
// ..do something that requires GLSL 1.2 or above.. | |||
} | |||
@endcode | |||
*/ | |||
static double getLanguageVersion(); | |||
/** Compiles and adds a shader to this program. | |||
After adding all your shaders, remember to call link() to link them into | |||
a usable program. | |||
If your app is built in debug mode, this method will assert if the program | |||
fails to compile correctly. | |||
The shaderType parameter could be GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc. | |||
@returns true if the shader compiled successfully. If not, you can call | |||
getLastError() to find out what happened. | |||
*/ | |||
bool addShader (const String& shaderSourceCode, GLenum shaderType); | |||
/** Compiles and adds a fragment shader to this program. | |||
This is equivalent to calling addShader() with a type of GL_VERTEX_SHADER. | |||
*/ | |||
bool addVertexShader (const String& shaderSourceCode); | |||
/** Compiles and adds a fragment shader to this program. | |||
This is equivalent to calling addShader() with a type of GL_FRAGMENT_SHADER. | |||
*/ | |||
bool addFragmentShader (const String& shaderSourceCode); | |||
/** Links all the compiled shaders into a usable program. | |||
If your app is built in debug mode, this method will assert if the program | |||
fails to link correctly. | |||
@returns true if the program linked successfully. If not, you can call | |||
getLastError() to find out what happened. | |||
*/ | |||
bool link() noexcept; | |||
/** Get the output for the last shader compilation or link that failed. */ | |||
const String& getLastError() const noexcept { return errorLog; } | |||
/** Selects this program into the current context. */ | |||
void use() const noexcept; | |||
/** Deletes the program. */ | |||
void release() noexcept; | |||
//============================================================================== | |||
// Methods for setting shader uniforms without using a Uniform object (see below). | |||
// You must make sure this shader is the currently bound one before setting uniforms | |||
// with these functions. | |||
/** Get the uniform ID from the variable name */ | |||
GLint getUniformIDFromName (const char* uniformName) const noexcept; | |||
/** Sets a float uniform. */ | |||
void setUniform (const char* uniformName, GLfloat value) noexcept; | |||
/** Sets an int uniform. */ | |||
void setUniform (const char* uniformName, GLint value) noexcept; | |||
/** Sets a vec2 uniform. */ | |||
void setUniform (const char* uniformName, GLfloat x, GLfloat y) noexcept; | |||
/** Sets a vec3 uniform. */ | |||
void setUniform (const char* uniformName, GLfloat x, GLfloat y, GLfloat z) noexcept; | |||
/** Sets a vec4 uniform. */ | |||
void setUniform (const char* uniformName, GLfloat x, GLfloat y, GLfloat z, GLfloat w) noexcept; | |||
/** Sets a vec4 uniform. */ | |||
void setUniform (const char* uniformName, GLint x, GLint y, GLint z, GLint w) noexcept; | |||
/** Sets a vector float uniform. */ | |||
void setUniform (const char* uniformName, const GLfloat* values, GLsizei numValues) noexcept; | |||
/** Sets a 2x2 matrix float uniform. */ | |||
void setUniformMat2 (const char* uniformName, const GLfloat* values, GLint count, GLboolean transpose) noexcept; | |||
/** Sets a 3x3 matrix float uniform. */ | |||
void setUniformMat3 (const char* uniformName, const GLfloat* values, GLint count, GLboolean transpose) noexcept; | |||
/** Sets a 4x4 matrix float uniform. */ | |||
void setUniformMat4 (const char* uniformName, const GLfloat* values, GLint count, GLboolean transpose) noexcept; | |||
//============================================================================== | |||
/** Represents an openGL uniform value. | |||
After a program has been linked, you can create Uniform objects to let you | |||
set the uniforms that your shaders use. | |||
Be careful not to call the set() functions unless the appropriate program | |||
is loaded into the current context. | |||
*/ | |||
struct JUCE_API Uniform | |||
{ | |||
/** Initialises a uniform. | |||
The program must have been successfully linked when this | |||
constructor is called. | |||
*/ | |||
Uniform (const OpenGLShaderProgram& program, const char* uniformName); | |||
/** Sets a float uniform. */ | |||
void set (GLfloat n1) const noexcept; | |||
/** Sets an int uniform. */ | |||
void set (GLint n1) const noexcept; | |||
/** Sets a vec2 uniform. */ | |||
void set (GLfloat n1, GLfloat n2) const noexcept; | |||
/** Sets a vec3 uniform. */ | |||
void set (GLfloat n1, GLfloat n2, GLfloat n3) const noexcept; | |||
/** Sets a vec4 uniform. */ | |||
void set (GLfloat n1, GLfloat n2, GLfloat n3, float n4) const noexcept; | |||
/** Sets an ivec4 uniform. */ | |||
void set (GLint n1, GLint n2, GLint n3, GLint n4) const noexcept; | |||
/** Sets a vector float uniform. */ | |||
void set (const GLfloat* values, int numValues) const noexcept; | |||
/** Sets a 2x2 matrix float uniform. */ | |||
void setMatrix2 (const GLfloat* values, GLint count, GLboolean transpose) const noexcept; | |||
/** Sets a 3x3 matrix float uniform. */ | |||
void setMatrix3 (const GLfloat* values, GLint count, GLboolean transpose) const noexcept; | |||
/** Sets a 4x4 matrix float uniform. */ | |||
void setMatrix4 (const GLfloat* values, GLint count, GLboolean transpose) const noexcept; | |||
/** The uniform's ID number. | |||
If the uniform couldn't be found, this value will be < 0. | |||
*/ | |||
GLint uniformID; | |||
private: | |||
const OpenGLContext& context; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Uniform) | |||
}; | |||
//============================================================================== | |||
/** Represents an openGL vertex attribute value. | |||
After a program has been linked, you can create Attribute objects to let you | |||
set the attributes that your vertex shaders use. | |||
*/ | |||
struct JUCE_API Attribute | |||
{ | |||
/** Initialises an attribute. | |||
The program must have been successfully linked when this | |||
constructor is called. | |||
*/ | |||
Attribute (const OpenGLShaderProgram&, const char* attributeName); | |||
/** The attribute's ID number. | |||
If the uniform couldn't be found, this value will be < 0. | |||
*/ | |||
GLuint attributeID; | |||
}; | |||
/** The ID number of the compiled program. */ | |||
GLuint getProgramID() const noexcept; | |||
private: | |||
const OpenGLContext& context; | |||
mutable GLuint programID = 0; | |||
String errorLog; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLShaderProgram) | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,192 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
static int getAllowedTextureSize (int x) | |||
{ | |||
#if JUCE_OPENGL_ALLOW_NON_POWER_OF_TWO_TEXTURES | |||
return x; | |||
#else | |||
return nextPowerOfTwo (x); | |||
#endif | |||
} | |||
OpenGLTexture::OpenGLTexture() | |||
: textureID (0), width (0), height (0), ownerContext (nullptr) | |||
{ | |||
} | |||
OpenGLTexture::~OpenGLTexture() | |||
{ | |||
release(); | |||
} | |||
bool OpenGLTexture::isValidSize (int width, int height) | |||
{ | |||
return isPowerOfTwo (width) && isPowerOfTwo (height); | |||
} | |||
void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum type, bool topLeft) | |||
{ | |||
ownerContext = OpenGLContext::getCurrentContext(); | |||
// Texture objects can only be created when the current thread has an active OpenGL | |||
// context. You'll need to create this object in one of the OpenGLContext's callbacks. | |||
jassert (ownerContext != nullptr); | |||
if (textureID == 0) | |||
{ | |||
JUCE_CHECK_OPENGL_ERROR | |||
glGenTextures (1, &textureID); | |||
glBindTexture (GL_TEXTURE_2D, textureID); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
else | |||
{ | |||
glBindTexture (GL_TEXTURE_2D, textureID); | |||
JUCE_CHECK_OPENGL_ERROR; | |||
} | |||
glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | |||
JUCE_CHECK_OPENGL_ERROR | |||
width = getAllowedTextureSize (w); | |||
height = getAllowedTextureSize (h); | |||
const GLint internalformat = type == GL_ALPHA ? GL_ALPHA : GL_RGBA; | |||
if (width != w || height != h) | |||
{ | |||
glTexImage2D (GL_TEXTURE_2D, 0, internalformat, | |||
width, height, 0, type, GL_UNSIGNED_BYTE, nullptr); | |||
glTexSubImage2D (GL_TEXTURE_2D, 0, 0, topLeft ? (height - h) : 0, w, h, | |||
type, GL_UNSIGNED_BYTE, pixels); | |||
} | |||
else | |||
{ | |||
glTexImage2D (GL_TEXTURE_2D, 0, internalformat, | |||
w, h, 0, type, GL_UNSIGNED_BYTE, pixels); | |||
} | |||
JUCE_CHECK_OPENGL_ERROR | |||
} | |||
template <class PixelType> | |||
struct Flipper | |||
{ | |||
static void flip (HeapBlock<PixelARGB>& dataCopy, const uint8* srcData, const int lineStride, | |||
const int w, const int h) | |||
{ | |||
dataCopy.malloc ((size_t) (w * h)); | |||
for (int y = 0; y < h; ++y) | |||
{ | |||
const PixelType* src = (const PixelType*) srcData; | |||
PixelARGB* const dst = (PixelARGB*) (dataCopy + w * (h - 1 - y)); | |||
for (int x = 0; x < w; ++x) | |||
dst[x].set (src[x]); | |||
srcData += lineStride; | |||
} | |||
} | |||
}; | |||
void OpenGLTexture::loadImage (const Image& image) | |||
{ | |||
const int imageW = image.getWidth(); | |||
const int imageH = image.getHeight(); | |||
HeapBlock<PixelARGB> dataCopy; | |||
Image::BitmapData srcData (image, Image::BitmapData::readOnly); | |||
switch (srcData.pixelFormat) | |||
{ | |||
case Image::ARGB: Flipper<PixelARGB> ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH); break; | |||
case Image::RGB: Flipper<PixelRGB> ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH); break; | |||
case Image::SingleChannel: Flipper<PixelAlpha>::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH); break; | |||
default: break; | |||
} | |||
create (imageW, imageH, dataCopy, JUCE_RGBA_FORMAT, true); | |||
} | |||
void OpenGLTexture::loadARGB (const PixelARGB* pixels, const int w, const int h) | |||
{ | |||
create (w, h, pixels, JUCE_RGBA_FORMAT, false); | |||
} | |||
void OpenGLTexture::loadAlpha (const uint8* pixels, int w, int h) | |||
{ | |||
create (w, h, pixels, GL_ALPHA, false); | |||
} | |||
void OpenGLTexture::loadARGBFlipped (const PixelARGB* pixels, int w, int h) | |||
{ | |||
HeapBlock<PixelARGB> flippedCopy; | |||
Flipper<PixelARGB>::flip (flippedCopy, (const uint8*) pixels, 4 * w, w, h); | |||
create (w, h, flippedCopy, JUCE_RGBA_FORMAT, true); | |||
} | |||
void OpenGLTexture::release() | |||
{ | |||
if (textureID != 0) | |||
{ | |||
// If the texture is deleted while the owner context is not active, it's | |||
// impossible to delete it, so this will be a leak until the context itself | |||
// is deleted. | |||
jassert (ownerContext == OpenGLContext::getCurrentContext()); | |||
if (ownerContext == OpenGLContext::getCurrentContext()) | |||
{ | |||
glDeleteTextures (1, &textureID); | |||
textureID = 0; | |||
width = 0; | |||
height = 0; | |||
} | |||
} | |||
} | |||
void OpenGLTexture::bind() const | |||
{ | |||
glBindTexture (GL_TEXTURE_2D, textureID); | |||
} | |||
void OpenGLTexture::unbind() const | |||
{ | |||
glBindTexture (GL_TEXTURE_2D, 0); | |||
} | |||
} // namespace juce |
@@ -0,0 +1,103 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
Creates an openGL texture from an Image. | |||
*/ | |||
class JUCE_API OpenGLTexture | |||
{ | |||
public: | |||
OpenGLTexture(); | |||
~OpenGLTexture(); | |||
/** Creates a texture from the given image. | |||
Note that if the image's dimensions aren't a power-of-two, the texture may | |||
be created with a larger size. | |||
The image will be arranged so that its top-left corner is at texture | |||
coordinate (0, 1). | |||
*/ | |||
void loadImage (const Image& image); | |||
/** Creates a texture from a raw array of pixels. | |||
If width and height are not powers-of-two, the texture will be created with a | |||
larger size, and only the subsection (0, 0, width, height) will be initialised. | |||
The data is sent directly to the OpenGL driver without being flipped vertically, | |||
so the first pixel will be mapped onto texture coordinate (0, 0). | |||
*/ | |||
void loadARGB (const PixelARGB* pixels, int width, int height); | |||
/** Creates a texture from a raw array of pixels. | |||
This is like loadARGB, but will vertically flip the data so that the first | |||
pixel ends up at texture coordinate (0, 1), and if the width and height are | |||
not powers-of-two, it will compensate by using a larger texture size. | |||
*/ | |||
void loadARGBFlipped (const PixelARGB* pixels, int width, int height); | |||
/** Creates an alpha-channel texture from an array of alpha values. | |||
If width and height are not powers-of-two, the texture will be created with a | |||
larger size, and only the subsection (0, 0, width, height) will be initialised. | |||
The data is sent directly to the OpenGL driver without being flipped vertically, | |||
so the first pixel will be mapped onto texture coordinate (0, 0). | |||
*/ | |||
void loadAlpha (const uint8* pixels, int width, int height); | |||
/** Frees the texture, if there is one. */ | |||
void release(); | |||
/** Binds the texture to the currently active openGL context. */ | |||
void bind() const; | |||
/** Unbinds the texture to the currently active openGL context. */ | |||
void unbind() const; | |||
/** Returns the GL texture ID number. */ | |||
GLuint getTextureID() const noexcept { return textureID; } | |||
int getWidth() const noexcept { return width; } | |||
int getHeight() const noexcept { return height; } | |||
/** Returns true if a texture can be created with the given size. | |||
Some systems may require that the sizes are powers-of-two. | |||
*/ | |||
static bool isValidSize (int width, int height); | |||
private: | |||
GLuint textureID; | |||
int width, height; | |||
OpenGLContext* ownerContext; | |||
void create (int w, int h, const void*, GLenum, bool topLeft); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture) | |||
}; | |||
} // namespace juce |
@@ -0,0 +1,70 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
OpenGLAppComponent::OpenGLAppComponent() | |||
{ | |||
setOpaque (true); | |||
openGLContext.setRenderer (this); | |||
openGLContext.attachTo (*this); | |||
openGLContext.setContinuousRepainting (true); | |||
} | |||
OpenGLAppComponent::~OpenGLAppComponent() | |||
{ | |||
// Before your subclass's destructor has completed, you must call | |||
// shutdownOpenGL() to release the GL context. (Otherwise there's | |||
// a danger that it may invoke a GL callback on your class while | |||
// it's in the process of being deleted. | |||
jassert (! openGLContext.isAttached()); | |||
shutdownOpenGL(); | |||
} | |||
void OpenGLAppComponent::shutdownOpenGL() | |||
{ | |||
openGLContext.detach(); | |||
} | |||
void OpenGLAppComponent::newOpenGLContextCreated() | |||
{ | |||
initialise(); | |||
} | |||
void OpenGLAppComponent::renderOpenGL() | |||
{ | |||
++frameCounter; | |||
render(); | |||
} | |||
void OpenGLAppComponent::openGLContextClosing() | |||
{ | |||
shutdown(); | |||
} | |||
} // namespace juce |
@@ -0,0 +1,96 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
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 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-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 | |||
{ | |||
//============================================================================== | |||
/** | |||
A base class for writing simple one-page graphical apps. | |||
A subclass can inherit from this and implement just a few methods such as | |||
paint() and mouse-handling. The base class provides some simple abstractions | |||
to take care of continuously repainting itself. | |||
*/ | |||
class JUCE_API OpenGLAppComponent : public Component, | |||
private OpenGLRenderer | |||
{ | |||
public: | |||
OpenGLAppComponent(); | |||
/** Destructor. */ | |||
~OpenGLAppComponent(); | |||
/** Returns the number of times that the render method has been called since | |||
the component started running. | |||
*/ | |||
int getFrameCounter() const noexcept { return frameCounter; } | |||
/** This must be called from your subclass's destructor, to shut down | |||
the GL system and stop it calling render() before your class is destroyed. | |||
*/ | |||
void shutdownOpenGL(); | |||
/** Implement this method to set up any GL objects that you need for rendering. | |||
The GL context will be active when this method is called. | |||
Note that because the GL context could be destroyed and re-created ad-hoc by | |||
the underlying platform, the shutdown() and initialise() calls could be called | |||
multiple times while your app is running. So don't make your code assume that | |||
this will only be called once! | |||
*/ | |||
virtual void initialise() = 0; | |||
/** Implement this method to free any GL objects that you created during rendering. | |||
The GL context will still be active when this method is called. | |||
Note that because the GL context could be destroyed and re-created ad-hoc by | |||
the underlying platform, the shutdown() and initialise() calls could be called | |||
multiple times while your app is running. So don't make your code assume that | |||
this will only be called once! | |||
*/ | |||
virtual void shutdown() = 0; | |||
/** Called to render your openGL. | |||
@see OpenGLRenderer::render() | |||
*/ | |||
virtual void render() = 0; | |||
/** The GL context */ | |||
OpenGLContext openGLContext; | |||
private: | |||
//============================================================================== | |||
int frameCounter = 0; | |||
void newOpenGLContextCreated() override; | |||
void renderOpenGL() override; | |||
void openGLContextClosing() override; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLAppComponent) | |||
}; | |||
} // namespace juce |