Browse Source

Add juce_dsp and juce_opengl modules here

tags/2020-07-14
falkTX 5 years ago
parent
commit
a89fc2af89
84 changed files with 21770 additions and 16 deletions
  1. +2
    -0
      libs/juce-plugin/JucePluginMain.h
  2. +79
    -15
      libs/juce/build-juce/AppConfig.h
  3. +3
    -1
      libs/juce/build-juce/premake.lua
  4. +549
    -0
      libs/juce/source/modules/juce_dsp/containers/juce_AudioBlock.h
  5. +414
    -0
      libs/juce/source/modules/juce_dsp/containers/juce_SIMDRegister.h
  6. +676
    -0
      libs/juce/source/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp
  7. +625
    -0
      libs/juce/source/modules/juce_dsp/filter_design/juce_FilterDesign.cpp
  8. +266
    -0
      libs/juce/source/modules/juce_dsp/filter_design/juce_FilterDesign.h
  9. +1150
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_Convolution.cpp
  10. +145
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_Convolution.h
  11. +845
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_FFT.cpp
  12. +120
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_FFT.h
  13. +213
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_FFT_test.cpp
  14. +194
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_Windowing.cpp
  15. +80
    -0
      libs/juce/source/modules/juce_dsp/frequency/juce_Windowing.h
  16. +84
    -0
      libs/juce/source/modules/juce_dsp/juce_dsp.cpp
  17. +265
    -0
      libs/juce/source/modules/juce_dsp/juce_dsp.h
  18. +27
    -0
      libs/juce/source/modules/juce_dsp/juce_dsp.mm
  19. +263
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_FastMathApproximations.h
  20. +157
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_LookupTable.cpp
  21. +328
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_LookupTable.h
  22. +318
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_Matrix.cpp
  23. +253
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_Matrix.h
  24. +172
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_Matrix_test.cpp
  25. +169
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_Polynomial.h
  26. +142
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_SpecialFunctions.cpp
  27. +66
    -0
      libs/juce/source/modules/juce_dsp/maths/juce_SpecialFunctions.h
  28. +59
    -0
      libs/juce/source/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp
  29. +567
    -0
      libs/juce/source/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h
  30. +220
    -0
      libs/juce/source/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h
  31. +44
    -0
      libs/juce/source/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp
  32. +394
    -0
      libs/juce/source/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h
  33. +59
    -0
      libs/juce/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp
  34. +748
    -0
      libs/juce/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h
  35. +146
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_Bias.h
  36. +162
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_FIRFilter.cpp
  37. +283
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_FIRFilter.h
  38. +210
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_FIRFilter_test.cpp
  39. +135
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_Gain.h
  40. +494
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_IIRFilter.cpp
  41. +291
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_IIRFilter.h
  42. +227
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_IIRFilter_Impl.h
  43. +162
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_Oscillator.h
  44. +718
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_Oversampling.cpp
  45. +146
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_Oversampling.h
  46. +162
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_ProcessContext.h
  47. +121
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_ProcessorChain.h
  48. +97
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_ProcessorDuplicator.h
  49. +78
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_ProcessorWrapper.h
  50. +214
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_StateVariableFilter.h
  51. +72
    -0
      libs/juce/source/modules/juce_dsp/processors/juce_WaveShaper.h
  52. +158
    -0
      libs/juce/source/modules/juce_opengl/geometry/juce_Draggable3DOrientation.h
  53. +155
    -0
      libs/juce/source/modules/juce_opengl/geometry/juce_Matrix3D.h
  54. +97
    -0
      libs/juce/source/modules/juce_opengl/geometry/juce_Quaternion.h
  55. +82
    -0
      libs/juce/source/modules/juce_opengl/geometry/juce_Vector3D.h
  56. +293
    -0
      libs/juce/source/modules/juce_opengl/juce_opengl.cpp
  57. +187
    -0
      libs/juce/source/modules/juce_opengl/juce_opengl.h
  58. +27
    -0
      libs/juce/source/modules/juce_opengl/juce_opengl.mm
  59. +163
    -0
      libs/juce/source/modules/juce_opengl/native/juce_MissingGLDefinitions.h
  60. +158
    -0
      libs/juce/source/modules/juce_opengl/native/juce_OpenGLExtensions.h
  61. +312
    -0
      libs/juce/source/modules/juce_opengl/native/juce_OpenGL_android.h
  62. +311
    -0
      libs/juce/source/modules/juce_opengl/native/juce_OpenGL_ios.h
  63. +256
    -0
      libs/juce/source/modules/juce_opengl/native/juce_OpenGL_linux_X11.h
  64. +262
    -0
      libs/juce/source/modules/juce_opengl/native/juce_OpenGL_osx.h
  65. +273
    -0
      libs/juce/source/modules/juce_opengl/native/juce_OpenGL_win32.h
  66. +1246
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLContext.cpp
  67. +348
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLContext.h
  68. +354
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp
  69. +134
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h
  70. +1901
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp
  71. +96
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h
  72. +135
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp
  73. +73
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.h
  74. +207
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLImage.cpp
  75. +53
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLImage.h
  76. +68
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLPixelFormat.cpp
  77. +70
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLPixelFormat.h
  78. +80
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLRenderer.h
  79. +193
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLShaderProgram.cpp
  80. +203
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLShaderProgram.h
  81. +192
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp
  82. +103
    -0
      libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLTexture.h
  83. +70
    -0
      libs/juce/source/modules/juce_opengl/utils/juce_OpenGLAppComponent.cpp
  84. +96
    -0
      libs/juce/source/modules/juce_opengl/utils/juce_OpenGLAppComponent.h

+ 2
- 0
libs/juce-plugin/JucePluginMain.h View File

@@ -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

+ 79
- 15
libs/juce/build-juce/AppConfig.h View File

@@ -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


+ 3
- 1
libs/juce/build-juce/premake.lua View File

@@ -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

+ 549
- 0
libs/juce/source/modules/juce_dsp/containers/juce_AudioBlock.h View File

@@ -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

+ 414
- 0
libs/juce/source/modules/juce_dsp/containers/juce_SIMDRegister.h View File

@@ -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

+ 676
- 0
libs/juce/source/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp View File

@@ -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

+ 625
- 0
libs/juce/source/modules/juce_dsp/filter_design/juce_FilterDesign.cpp View File

@@ -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

+ 266
- 0
libs/juce/source/modules/juce_dsp/filter_design/juce_FilterDesign.h View File

@@ -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

+ 1150
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_Convolution.cpp
File diff suppressed because it is too large
View File


+ 145
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_Convolution.h View File

@@ -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

+ 845
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_FFT.cpp View File

@@ -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

+ 120
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_FFT.h View File

@@ -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

+ 213
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_FFT_test.cpp View File

@@ -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

+ 194
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_Windowing.cpp View File

@@ -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

+ 80
- 0
libs/juce/source/modules/juce_dsp/frequency/juce_Windowing.h View File

@@ -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

+ 84
- 0
libs/juce/source/modules/juce_dsp/juce_dsp.cpp View File

@@ -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

+ 265
- 0
libs/juce/source/modules/juce_dsp/juce_dsp.h View File

@@ -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"

+ 27
- 0
libs/juce/source/modules/juce_dsp/juce_dsp.mm View File

@@ -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"

+ 263
- 0
libs/juce/source/modules/juce_dsp/maths/juce_FastMathApproximations.h View File

@@ -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

+ 157
- 0
libs/juce/source/modules/juce_dsp/maths/juce_LookupTable.cpp View File

@@ -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

+ 328
- 0
libs/juce/source/modules/juce_dsp/maths/juce_LookupTable.h View File

@@ -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

+ 318
- 0
libs/juce/source/modules/juce_dsp/maths/juce_Matrix.cpp View File

@@ -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

+ 253
- 0
libs/juce/source/modules/juce_dsp/maths/juce_Matrix.h View File

@@ -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

+ 172
- 0
libs/juce/source/modules/juce_dsp/maths/juce_Matrix_test.cpp View File

@@ -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

+ 169
- 0
libs/juce/source/modules/juce_dsp/maths/juce_Polynomial.h View File

@@ -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

+ 142
- 0
libs/juce/source/modules/juce_dsp/maths/juce_SpecialFunctions.cpp View File

@@ -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

+ 66
- 0
libs/juce/source/modules/juce_dsp/maths/juce_SpecialFunctions.h View File

@@ -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

+ 59
- 0
libs/juce/source/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp View File

@@ -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 };
}
}

+ 567
- 0
libs/juce/source/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h View File

@@ -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

+ 220
- 0
libs/juce/source/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h View File

@@ -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

+ 44
- 0
libs/juce/source/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp View File

@@ -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 };
}
}

+ 394
- 0
libs/juce/source/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h View File

@@ -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

+ 59
- 0
libs/juce/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp View File

@@ -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 };
}
}

+ 748
- 0
libs/juce/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h View File

@@ -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

+ 146
- 0
libs/juce/source/modules/juce_dsp/processors/juce_Bias.h View File

@@ -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

+ 162
- 0
libs/juce/source/modules/juce_dsp/processors/juce_FIRFilter.cpp View File

@@ -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

+ 283
- 0
libs/juce/source/modules/juce_dsp/processors/juce_FIRFilter.h View File

@@ -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

+ 210
- 0
libs/juce/source/modules/juce_dsp/processors/juce_FIRFilter_test.cpp View File

@@ -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

+ 135
- 0
libs/juce/source/modules/juce_dsp/processors/juce_Gain.h View File

@@ -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

+ 494
- 0
libs/juce/source/modules/juce_dsp/processors/juce_IIRFilter.cpp View File

@@ -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

+ 291
- 0
libs/juce/source/modules/juce_dsp/processors/juce_IIRFilter.h View File

@@ -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"

+ 227
- 0
libs/juce/source/modules/juce_dsp/processors/juce_IIRFilter_Impl.h View File

@@ -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

+ 162
- 0
libs/juce/source/modules/juce_dsp/processors/juce_Oscillator.h View File

@@ -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

+ 718
- 0
libs/juce/source/modules/juce_dsp/processors/juce_Oversampling.cpp View File

@@ -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

+ 146
- 0
libs/juce/source/modules/juce_dsp/processors/juce_Oversampling.h View File

@@ -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

+ 162
- 0
libs/juce/source/modules/juce_dsp/processors/juce_ProcessContext.h View File

@@ -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

+ 121
- 0
libs/juce/source/modules/juce_dsp/processors/juce_ProcessorChain.h View File

@@ -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

+ 97
- 0
libs/juce/source/modules/juce_dsp/processors/juce_ProcessorDuplicator.h View File

@@ -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

+ 78
- 0
libs/juce/source/modules/juce_dsp/processors/juce_ProcessorWrapper.h View File

@@ -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

+ 214
- 0
libs/juce/source/modules/juce_dsp/processors/juce_StateVariableFilter.h View File

@@ -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

+ 72
- 0
libs/juce/source/modules/juce_dsp/processors/juce_WaveShaper.h View File

@@ -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

+ 158
- 0
libs/juce/source/modules/juce_opengl/geometry/juce_Draggable3DOrientation.h View File

@@ -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

+ 155
- 0
libs/juce/source/modules/juce_opengl/geometry/juce_Matrix3D.h View File

@@ -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

+ 97
- 0
libs/juce/source/modules/juce_opengl/geometry/juce_Quaternion.h View File

@@ -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

+ 82
- 0
libs/juce/source/modules/juce_opengl/geometry/juce_Vector3D.h View File

@@ -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

+ 293
- 0
libs/juce/source/modules/juce_opengl/juce_opengl.cpp View File

@@ -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"

+ 187
- 0
libs/juce/source/modules/juce_opengl/juce_opengl.h View File

@@ -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"

+ 27
- 0
libs/juce/source/modules/juce_opengl/juce_opengl.mm View File

@@ -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"

+ 163
- 0
libs/juce/source/modules/juce_opengl/native/juce_MissingGLDefinitions.h View File

@@ -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

+ 158
- 0
libs/juce/source/modules/juce_opengl/native/juce_OpenGLExtensions.h View File

@@ -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

+ 312
- 0
libs/juce/source/modules/juce_opengl/native/juce_OpenGL_android.h View File

@@ -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

+ 311
- 0
libs/juce/source/modules/juce_opengl/native/juce_OpenGL_ios.h View File

@@ -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

+ 256
- 0
libs/juce/source/modules/juce_opengl/native/juce_OpenGL_linux_X11.h View File

@@ -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

+ 262
- 0
libs/juce/source/modules/juce_opengl/native/juce_OpenGL_osx.h View File

@@ -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

+ 273
- 0
libs/juce/source/modules/juce_opengl/native/juce_OpenGL_win32.h View File

@@ -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

+ 1246
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLContext.cpp
File diff suppressed because it is too large
View File


+ 348
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLContext.h View File

@@ -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

+ 354
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp View File

@@ -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, &params);
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

+ 134
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h View File

@@ -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

+ 1901
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp
File diff suppressed because it is too large
View File


+ 96
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h View File

@@ -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

+ 135
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp View File

@@ -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

+ 73
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.h View File

@@ -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

+ 207
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLImage.cpp View File

@@ -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

+ 53
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLImage.h View File

@@ -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

+ 68
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLPixelFormat.cpp View File

@@ -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

+ 70
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLPixelFormat.h View File

@@ -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

+ 80
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLRenderer.h View File

@@ -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

+ 193
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLShaderProgram.cpp View File

@@ -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

+ 203
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLShaderProgram.h View File

@@ -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

+ 192
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp View File

@@ -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

+ 103
- 0
libs/juce/source/modules/juce_opengl/opengl/juce_OpenGLTexture.h View File

@@ -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

+ 70
- 0
libs/juce/source/modules/juce_opengl/utils/juce_OpenGLAppComponent.cpp View File

@@ -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

+ 96
- 0
libs/juce/source/modules/juce_opengl/utils/juce_OpenGLAppComponent.h View File

@@ -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

Loading…
Cancel
Save