Browse Source

DSP: Allowed AudioBlocks of const elements

tags/2021-05-28
reuk Tom Poole 6 years ago
parent
commit
827055c2c1
9 changed files with 142 additions and 58 deletions
  1. +22
    -0
      BREAKING-CHANGES.txt
  2. +102
    -36
      modules/juce_dsp/containers/juce_AudioBlock.h
  3. +0
    -7
      modules/juce_dsp/containers/juce_SIMDRegister.h
  4. +2
    -2
      modules/juce_dsp/frequency/juce_Convolution.cpp
  5. +1
    -1
      modules/juce_dsp/frequency/juce_Convolution.h
  6. +2
    -2
      modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h
  7. +2
    -2
      modules/juce_dsp/processors/juce_FIRFilter_test.cpp
  8. +9
    -6
      modules/juce_dsp/processors/juce_ProcessContext.h
  9. +2
    -2
      modules/juce_dsp/processors/juce_ProcessorDuplicator.h

+ 22
- 0
BREAKING-CHANGES.txt View File

@@ -4,6 +4,28 @@ JUCE breaking changes
Develop
=======

Change
------
The AudioBlock class now differentiates between const and non-const data.

Possible Issues
---------------
The return type of the getInputBlock() method of the ProcessContextReplacing
and ProcessContextNonReplacing classes has changed from AudioBlock<X> to
AudioBlock<const X>.

Workaround
----------
For ProcessContextReplacing you should use getOutputBlock() instead of
getInputBlock(). For ProcessContextNonReplacing attempting to modify the input
block is very likely an error.

Rationale
---------
This change makes the intent of the code much clearer and means that we can
remove some const_cast operations.


Change
------
The formatting of floating point numbers written to XML and JSON files has


+ 102
- 36
modules/juce_dsp/containers/juce_AudioBlock.h View File

@@ -32,10 +32,17 @@ 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; };
template <typename T, bool = std::is_floating_point<T>::value>
struct ElementType
{
using Type = T;
};
template <typename T>
struct ElementType<T, false>
{
using Type = typename T::value_type;
};
}
#endif
@@ -56,6 +63,15 @@ namespace SampleTypeHelpers // Internal classes needed for handling sample type
template <typename SampleType>
class AudioBlock
{
private:
template <typename OtherSampleType>
using MayUseConvertingConstructor =
std::enable_if_t<std::is_same<std::remove_const_t<SampleType>,
std::remove_const_t<OtherSampleType>>::value
&& std::is_const<SampleType>::value
&& ! std::is_const<OtherSampleType>::value,
int>;
public:
//==============================================================================
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
@@ -127,7 +143,8 @@ public:
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
template <typename OtherSampleType>
AudioBlock (AudioBuffer<OtherSampleType>& buffer) noexcept
: channels (buffer.getArrayOfWritePointers()),
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
numSamples (static_cast<size_t> (buffer.getNumSamples()))
@@ -139,7 +156,8 @@ public:
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
template <typename OtherSampleType>
AudioBlock (AudioBuffer<OtherSampleType>& buffer, size_t startSampleIndex) noexcept
: channels (buffer.getArrayOfWritePointers()),
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
startSample (startSampleIndex),
@@ -151,16 +169,45 @@ public:
AudioBlock (const AudioBlock& other) noexcept = default;
AudioBlock& operator= (const AudioBlock& other) noexcept = default;
template <typename OtherSampleType, MayUseConvertingConstructor<OtherSampleType> = 0>
AudioBlock (const AudioBlock<OtherSampleType>& other) noexcept
: channels { other.channels },
numChannels { other.numChannels },
startSample { other.startSample },
numSamples { other.numSamples }
{
}
template <typename OtherSampleType, MayUseConvertingConstructor<OtherSampleType> = 0>
AudioBlock& operator= (const AudioBlock<OtherSampleType>& other) noexcept
{
AudioBlock copy { other };
swap (copy);
return *this;
}
void swap (AudioBlock& other) noexcept
{
std::swap (other.channels, channels);
std::swap (other.numChannels, numChannels);
std::swap (other.startSample, startSample);
std::swap (other.numSamples, numSamples);
}
//==============================================================================
bool operator== (const AudioBlock& other) const noexcept
template <typename OtherSampleType>
bool operator== (const AudioBlock<OtherSampleType>& other) const noexcept
{
return std::equal (channels, channels + numChannels,
other.channels, other.channels + other.numChannels)
&& startSample == other.startSample
&& numSamples == other.numSamples;
return std::equal (channels,
channels + numChannels,
other.channels,
other.channels + other.numChannels)
&& startSample == other.startSample
&& numSamples == other.numSamples;
}
bool operator!= (const AudioBlock& other) const noexcept
template <typename OtherSampleType>
bool operator!= (const AudioBlock<OtherSampleType>& other) const noexcept
{
return ! (*this == other);
}
@@ -264,7 +311,8 @@ public:
}
/** Copy the values in src to the receiver. */
forcedinline AudioBlock& copy (AudioBlock src) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& copy (const AudioBlock<OtherSampleType>& src) noexcept
{
auto maxChannels = jmin (src.numChannels, numChannels);
auto n = static_cast<int> (jmin (src.numSamples, numSamples) * sizeFactor);
@@ -381,7 +429,8 @@ public:
}
/** Adds the source values to the receiver. */
forcedinline AudioBlock& add (AudioBlock src) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& add (AudioBlock<OtherSampleType> src) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -393,7 +442,8 @@ public:
}
/** Adds a fixed value to each source value and stores it in the destination array of the receiver. */
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE add (AudioBlock src, SampleType value) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE add (AudioBlock<OtherSampleType> src, SampleType value) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -405,7 +455,8 @@ public:
}
/** Adds each source1 value to the corresponding source2 value and stores it in the destination array of the receiver. */
forcedinline AudioBlock& add (AudioBlock src1, AudioBlock src2) noexcept
template <typename Src1SampleType, typename Src2SampleType>
forcedinline AudioBlock& add (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept
{
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
@@ -423,7 +474,8 @@ public:
}
/** Subtracts the source values from the receiver. */
forcedinline AudioBlock& subtract (AudioBlock src) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& subtract (AudioBlock<OtherSampleType> src) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -435,13 +487,15 @@ public:
}
/** Subtracts a fixed value from each source value and stores it in the destination array of the receiver. */
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE subtract (AudioBlock src, SampleType value) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE subtract (AudioBlock<OtherSampleType> 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 (AudioBlock src1, AudioBlock src2) noexcept
template <typename Src1SampleType, typename Src2SampleType>
forcedinline AudioBlock& subtract (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept
{
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
@@ -464,7 +518,8 @@ public:
}
/** Multiplies the source values to the receiver. */
forcedinline AudioBlock& multiply (AudioBlock src) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& multiply (AudioBlock<OtherSampleType> src) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -476,7 +531,8 @@ public:
}
/** Multiplies a fixed value to each source value and stores it in the destination array of the receiver. */
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE multiply (AudioBlock src, SampleType value) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE multiply (AudioBlock<OtherSampleType> src, SampleType value) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -488,7 +544,8 @@ public:
}
/** Multiplies each source1 value to the corresponding source2 value and stores it in the destination array of the receiver. */
forcedinline AudioBlock& multiply (AudioBlock src1, AudioBlock src2) noexcept
template <typename Src1SampleType, typename Src2SampleType>
forcedinline AudioBlock& multiply (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept
{
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
@@ -522,8 +579,8 @@ public:
}
/** Multiplies all channels of the source by a smoothly changing value and stores them in the receiver. */
template <typename SmoothingType>
AudioBlock& multiply (AudioBlock src, SmoothedValue<SampleType, SmoothingType>& value) noexcept
template <typename OtherSampleType, typename SmoothingType>
AudioBlock& multiply (AudioBlock<OtherSampleType> src, SmoothedValue<SampleType, SmoothingType>& value) noexcept
{
jassert (numChannels == src.numChannels);
@@ -548,7 +605,8 @@ public:
}
/** Multiplies each value in src with factor and adds the result to the receiver. */
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE addWithMultiply (AudioBlock src, SampleType factor) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& JUCE_VECTOR_CALLTYPE addWithMultiply (AudioBlock<OtherSampleType> src, SampleType factor) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -560,7 +618,8 @@ public:
}
/** Multiplies each value in srcA with the corresponding value in srcB and adds the result to the receiver. */
forcedinline AudioBlock& addWithMultiply (AudioBlock src1, AudioBlock src2) noexcept
template <typename Src1SampleType, typename Src2SampleType>
forcedinline AudioBlock& addWithMultiply (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept
{
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
@@ -578,7 +637,8 @@ public:
}
/** Negates each value of source and stores it in the receiver. */
forcedinline AudioBlock& replaceWithNegativeOf (AudioBlock src) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& replaceWithNegativeOf (AudioBlock<OtherSampleType> src) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -590,7 +650,8 @@ public:
}
/** Takes the absolute value of each element of src and stores it inside the receiver. */
forcedinline AudioBlock& replaceWithAbsoluteValueOf (AudioBlock src) noexcept
template <typename OtherSampleType>
forcedinline AudioBlock& replaceWithAbsoluteValueOf (AudioBlock<OtherSampleType> src) noexcept
{
jassert (numChannels == src.numChannels);
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
@@ -602,7 +663,8 @@ public:
}
/** Each element of receiver will be the minimum of the corresponding element of the source arrays. */
forcedinline AudioBlock& min (AudioBlock src1, AudioBlock src2) noexcept
template <typename Src1SampleType, typename Src2SampleType>
forcedinline AudioBlock& min (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept
{
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
auto n = static_cast<int> (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor);
@@ -614,7 +676,8 @@ public:
}
/** 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
template <typename Src1SampleType, typename Src2SampleType>
forcedinline AudioBlock& max (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept
{
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
auto n = static_cast<int> (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor);
@@ -653,11 +716,11 @@ public:
//==============================================================================
// This class can only be used with floating point types
static_assert (std::is_same<SampleType, float>::value
|| std::is_same<SampleType, double>::value
static_assert (std::is_same<std::remove_const_t<SampleType>, float>::value
|| std::is_same<std::remove_const_t<SampleType>, double>::value
#if JUCE_USE_SIMD
|| std::is_same<SampleType, SIMDRegister<float>>::value
|| std::is_same<SampleType, SIMDRegister<double>>::value
|| std::is_same<std::remove_const_t<SampleType>, SIMDRegister<float>>::value
|| std::is_same<std::remove_const_t<SampleType>, SIMDRegister<double>>::value
#endif
, "AudioBlock only supports single or double precision floating point types");
@@ -666,8 +729,8 @@ public:
The function supplied must take a SampleType as its parameter, and return a SampleType.
The two blocks must have the same number of channels and samples.
*/
template <typename FunctionType>
static void process (AudioBlock inBlock, AudioBlock outBlock, FunctionType&& function)
template <typename Src1SampleType, typename Src2SampleType, typename FunctionType>
static void process (AudioBlock<Src1SampleType> inBlock, AudioBlock<Src2SampleType> outBlock, FunctionType&& function)
{
auto len = inBlock.getNumSamples();
auto numChans = inBlock.getNumChannels();
@@ -707,6 +770,9 @@ private:
SampleType* const* channels;
ChannelCountType numChannels = 0;
size_t startSample = 0, numSamples = 0;
template <typename OtherSampleType>
friend class AudioBlock;
};
} // namespace dsp


+ 0
- 7
modules/juce_dsp/containers/juce_SIMDRegister.h View File

@@ -358,13 +358,6 @@ struct SIMDRegister
return snapPointerToAlignment (ptr, SIMDRegisterSize);
}
#ifndef DOXYGEN
static inline const ElementType* getNextSIMDAlignedPtr (const ElementType* ptr) noexcept
{
return snapPointerToAlignment (ptr, SIMDRegisterSize);
}
#endif
private:
static inline vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept
{


+ 2
- 2
modules/juce_dsp/frequency/juce_Convolution.cpp View File

@@ -718,7 +718,7 @@ struct Convolution::Pimpl : private Thread
/** Convolution processing handling interpolation between previous and new states
of the convolution engines.
*/
void processSamples (const AudioBlock<float>& input, AudioBlock<float>& output)
void processSamples (const AudioBlock<const float>& input, AudioBlock<float>& output)
{
processFifo();
@@ -1197,7 +1197,7 @@ void Convolution::reset() noexcept
pimpl->reset();
}
void Convolution::processSamples (const AudioBlock<float>& input, AudioBlock<float>& output, bool isBypassed) noexcept
void Convolution::processSamples (const AudioBlock<const float>& input, AudioBlock<float>& output, bool isBypassed) noexcept
{
if (! isActive)
return;


+ 1
- 1
modules/juce_dsp/frequency/juce_Convolution.h View File

@@ -156,7 +156,7 @@ private:
std::unique_ptr<Pimpl> pimpl;
//==============================================================================
void processSamples (const AudioBlock<float>&, AudioBlock<float>&, bool isBypassed) noexcept;
void processSamples (const AudioBlock<const float>&, AudioBlock<float>&, bool isBypassed) noexcept;
//==============================================================================
double sampleRate;


+ 2
- 2
modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h View File

@@ -43,8 +43,8 @@ namespace SIMDInternal
template <> struct MaskTypeFor <std::complex<float>> { using type = uint32_t; };
template <> struct MaskTypeFor <std::complex<double>> { using type = uint64_t; };
template <typename Primitive> struct PrimitiveType { using type = Primitive; };
template <typename Primitive> struct PrimitiveType<std::complex<Primitive>> { using type = Primitive; };
template <typename Primitive> struct PrimitiveType { using type = typename std::remove_cv<Primitive>::type; };
template <typename Primitive> struct PrimitiveType<std::complex<Primitive>> { using type = typename std::remove_cv<Primitive>::type; };
template <int n> struct Log2Helper { enum { value = Log2Helper<n/2>::value + 1 }; };
template <> struct Log2Helper<1> { enum { value = 0 }; };


+ 2
- 2
modules/juce_dsp/processors/juce_FIRFilter_test.cpp View File

@@ -121,7 +121,7 @@ class FIRFilterTest : public UnitTest
template <typename FloatType>
static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
{
AudioBlock<FloatType> input (&src, 1, n);
AudioBlock<const FloatType> input (&src, 1, n);
AudioBlock<FloatType> output (&dst, 1, n);
ProcessContextNonReplacing<FloatType> context (input, output);
@@ -151,7 +151,7 @@ class FIRFilterTest : public UnitTest
auto* src = input + i;
auto* dst = output + i;
AudioBlock<FloatType> inBlock (&src, 1, len);
AudioBlock<const FloatType> inBlock (&src, 1, len);
AudioBlock<FloatType> outBlock (&dst, 1, len);
ProcessContextNonReplacing<FloatType> context (inBlock, outBlock);


+ 9
- 6
modules/juce_dsp/processors/juce_ProcessContext.h View File

@@ -83,6 +83,7 @@ public:
using SampleType = ContextSampleType;
/** The type of audio block that this context handles. */
using AudioBlockType = AudioBlock<SampleType>;
using ConstAudioBlockType = AudioBlock<const SampleType>;
/** Creates a ProcessContextReplacing that uses the given audio block.
Note that the caller must not delete the block while it is still in use by this object!
@@ -93,10 +94,10 @@ public:
ProcessContextReplacing (ProcessContextReplacing&&) = default;
/** Returns the audio block to use as the input to a process function. */
const AudioBlockType& getInputBlock() const noexcept { return ioBlock; }
const ConstAudioBlockType& getInputBlock() const noexcept { return constBlock; }
/** Returns the audio block to use as the output to a process function. */
AudioBlockType& getOutputBlock() const noexcept { return const_cast<AudioBlockType&> (ioBlock); }
AudioBlockType& getOutputBlock() const noexcept { return ioBlock; }
/** All process context classes will define this constant method so that templated
code can determine whether the input and output blocks refer to the same buffer,
@@ -111,6 +112,7 @@ public:
private:
AudioBlockType& ioBlock;
ConstAudioBlockType constBlock { ioBlock };
};
//==============================================================================
@@ -134,11 +136,12 @@ public:
using SampleType = ContextSampleType;
/** The type of audio block that this context handles. */
using AudioBlockType = AudioBlock<SampleType>;
using ConstAudioBlockType = AudioBlock<const SampleType>;
/** Creates a ProcessContextReplacing that uses the given input and output blocks.
Note that the caller must not delete these blocks while they are still in use by this object!
*/
ProcessContextNonReplacing (const AudioBlockType& input, AudioBlockType& output) noexcept
ProcessContextNonReplacing (const ConstAudioBlockType& input, AudioBlockType& output) noexcept
: inputBlock (input), outputBlock (output)
{
// If the input and output blocks are the same then you should use
@@ -150,10 +153,10 @@ public:
ProcessContextNonReplacing (ProcessContextNonReplacing&&) = default;
/** Returns the audio block to use as the input to a process function. */
const AudioBlockType& getInputBlock() const noexcept { return inputBlock; }
const ConstAudioBlockType& getInputBlock() const noexcept { return inputBlock; }
/** Returns the audio block to use as the output to a process function. */
AudioBlockType& getOutputBlock() const noexcept { return const_cast<AudioBlockType&> (outputBlock); }
AudioBlockType& getOutputBlock() const noexcept { return outputBlock; }
/** All process context classes will define this constant method so that templated
code can determine whether the input and output blocks refer to the same buffer,
@@ -167,7 +170,7 @@ public:
bool isBypassed = false;
private:
const AudioBlockType& inputBlock;
const ConstAudioBlockType& inputBlock;
AudioBlockType& outputBlock;
};


+ 2
- 2
modules/juce_dsp/processors/juce_ProcessorDuplicator.h View File

@@ -89,8 +89,8 @@ private:
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); }
typename ProcessContext::ConstAudioBlockType getInputBlock() const noexcept { return ProcessContext::getInputBlock() .getSingleChannelBlock (channel); }
typename ProcessContext::AudioBlockType getOutputBlock() const noexcept { return ProcessContext::getOutputBlock().getSingleChannelBlock (channel); }
};
juce::OwnedArray<MonoProcessorType> processors;


Loading…
Cancel
Save