Skip to content

Commit

Permalink
Tweaks for Buffer helpers and SmoothedBufferValue (#469)
Browse files Browse the repository at this point in the history
* Tweaks for Buffer helpers and SmoothedBufferValue

* CI fixes
  • Loading branch information
jatinchowdhury18 authored Nov 17, 2023
1 parent f930081 commit 4482306
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 34 deletions.
12 changes: 7 additions & 5 deletions modules/dsp/chowdsp_buffers/Buffers/chowdsp_BufferView.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ class BufferView
/** The sample type used by the buffer */
using Type = SampleType;

BufferView& operator= (const BufferView<SampleType>&) = delete;
BufferView (BufferView<SampleType>&&) = delete;
BufferView& operator= (BufferView<SampleType>&&) = delete;
BufferView() = default;

BufferView& operator= (const BufferView<SampleType>&) = default;
BufferView (BufferView<SampleType>&&) noexcept = default;
BufferView& operator= (BufferView<SampleType>&&) noexcept = default;

BufferView (SampleType* const* data, int dataNumChannels, int dataNumSamples, int sampleOffset = 0) : numChannels (dataNumChannels),
numSamples (dataNumSamples)
Expand Down Expand Up @@ -253,8 +255,8 @@ class BufferView
channelPointers[ch] = data[ch + (size_t) startChannel] + sampleOffset;
}

const int numChannels = 1;
const int numSamples;
int numChannels = 1;
int numSamples = 0;

// Assuming we will never need an audio buffer with more than 64 channels.
// Maybe we'll need to increase this is we're doing high-order ambisonics or something?
Expand Down
63 changes: 41 additions & 22 deletions modules/dsp/chowdsp_buffers/Buffers/chowdsp_SIMDBufferHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,23 @@ namespace chowdsp
{
#if ! CHOWDSP_NO_XSIMD
/**
* Copies data from a chowdsp::Buffer of some scalar type,
* to a chowdsp::Buffer of the corresponding SIMD type.
* Copies data from a chowdsp::BufferView of some scalar type,
* to a chowdsp::BufferView of the corresponding SIMD type.
*
* The SIMD buffer must have at least enough memory allocated
* to store all of the data in the scalar buffer. The SIMD buffer
* will be resized (without allocating) to match the size of
* the scalar buffer. If the number of channels in the scalar
* The SIMD BufferView is expected to be the correct size to
* hold the SIMD data. If the number of channels in the scalar
* buffer does not divide evenly into the SIMD register size,
* the SIMD buffer will be padded with some extra channels,
* containing zeros.
*/
template <typename T1, typename T2>
[[maybe_unused]] static void copyToSIMDBuffer (const BufferView<const T1>& scalarBuffer, Buffer<xsimd::batch<T2>>& simdBuffer) noexcept
template <typename T1, typename T2 = T1>
[[maybe_unused]] static void copyToSIMDBuffer (const BufferView<const T1>& scalarBuffer, const BufferView<xsimd::batch<T2>>& simdBuffer) noexcept
{
using Vec = xsimd::batch<T2>;
static constexpr auto vecSize = (int) Vec::size;

const auto numSamples = scalarBuffer.getNumSamples();
const auto numScalarChannels = scalarBuffer.getNumChannels();
const auto numSIMDChannels = buffers_detail::ceiling_divide (numScalarChannels, vecSize);

const auto interleaveSamples = [numSamples] (const T1** source, T2* dest, int numChannels)
{
Expand All @@ -42,8 +39,6 @@ template <typename T1, typename T2>
}
};

simdBuffer.setCurrentSize (numSIMDChannels, numSamples);

for (int ch = 0; ch < numScalarChannels; ch += vecSize)
{
const auto channelsToInterleave = juce::jmin (vecSize, numScalarChannels - ch);
Expand All @@ -60,25 +55,49 @@ template <typename T1, typename T2>
}
}

/**
* Copies data from a chowdsp::BufferView of some scalar type,
* to a chowdsp::Buffer of the corresponding SIMD type.
*
* The SIMD buffer must have at least enough memory allocated
* to store all of the data in the scalar buffer. The SIMD buffer
* will be resized (without allocating) to match the size of
* the scalar buffer. If the number of channels in the scalar
* buffer does not divide evenly into the SIMD register size,
* the SIMD buffer will be padded with some extra channels,
* containing zeros.
*/
template <typename T1, typename T2 = T1>
[[maybe_unused]] static void copyToSIMDBuffer (const BufferView<const T1>& scalarBuffer, Buffer<xsimd::batch<T2>>& simdBuffer) noexcept
{
using Vec = xsimd::batch<T2>;
static constexpr auto vecSize = (int) Vec::size;

const auto numSamples = scalarBuffer.getNumSamples();
const auto numScalarChannels = scalarBuffer.getNumChannels();
const auto numSIMDChannels = buffers_detail::ceiling_divide (numScalarChannels, vecSize);

simdBuffer.setCurrentSize (numSIMDChannels, numSamples);

copyToSIMDBuffer (scalarBuffer, BufferView { simdBuffer });
}

/**
* Copies data from a chowdsp::Buffer of some scalar type,
* to a chowdsp::Buffer of the corresponding SIMD type.
*/
template <typename T1, typename T2>
template <typename T1, typename T2 = T1>
[[maybe_unused]] static void copyToSIMDBuffer (const Buffer<T1>& scalarBuffer, Buffer<xsimd::batch<T2>>& simdBuffer) noexcept
{
copyToSIMDBuffer (static_cast<const BufferView<const T1>&> (scalarBuffer), simdBuffer);
}

/**
* Copies data from a chowdsp::Buffer of some SIMD type,
* to a chowdsp::Buffer of the corresponding scalar type.
*
* The scalar buffer will NOT be resized to match the size
* of the SIMD buffer.
* Copies data from a chowdsp::BufferView of some SIMD type,
* to a chowdsp::BufferView of the corresponding scalar type.
*/
template <typename T1, typename T2>
[[maybe_unused]] static void copyFromSIMDBuffer (const Buffer<xsimd::batch<T2>>& simdBuffer, const BufferView<T1>& scalarBuffer) noexcept
template <typename T1, typename T2 = T1>
[[maybe_unused]] static void copyFromSIMDBuffer (const BufferView<const xsimd::batch<T2>>& simdBuffer, const BufferView<T1>& scalarBuffer) noexcept
{
using Vec = xsimd::batch<T2>;
static constexpr auto vecSize = (int) Vec::size;
Expand Down Expand Up @@ -106,7 +125,7 @@ template <typename T1, typename T2>

for (int ch = 0; ch < numSIMDChannels; ++ch)
{
const auto channelsToDeinterleave = juce::jmin (vecSize, scalarBuffer.getNumChannels() - ch * vecSize);
const auto channelsToDeinterleave = std::min (vecSize, scalarBuffer.getNumChannels() - ch * vecSize);

T1* scalarChannelPointers[(size_t) vecSize] {};
for (int i = 0; i < channelsToDeinterleave; ++i)
Expand All @@ -124,10 +143,10 @@ template <typename T1, typename T2>
* Copies data from a chowdsp::Buffer of some SIMD type,
* to a chowdsp::Buffer of the corresponding scalar type.
*/
template <typename T1, typename T2>
template <typename T1, typename T2 = T1>
[[maybe_unused]] static void copyFromSIMDBuffer (const Buffer<xsimd::batch<T2>>& simdBuffer, Buffer<T1>& scalarBuffer) noexcept
{
copyFromSIMDBuffer (simdBuffer, static_cast<const BufferView<T1>&> (scalarBuffer));
copyFromSIMDBuffer<T1, T2> (simdBuffer, static_cast<const BufferView<T1>&> (scalarBuffer));
}
#endif // ! CHOWDSP_NO_XSIMD
} // namespace chowdsp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

namespace chowdsp
{
#if ! CHOWDSP_NO_XSIMD
constexpr auto bufferAlignment = xsimd::default_arch::alignment();
#else
constexpr size_t bufferAlignment = 16;
#endif

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::setParameterHandle (std::atomic<float>* handle)
{
Expand All @@ -23,10 +29,14 @@ void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::setParameterHandle ([[
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::prepare (double fs, int samplesPerBlock)
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::prepare (double fs, int samplesPerBlock, bool useInternalVector)
{
sampleRate = fs;
buffer.resize ((size_t) samplesPerBlock, {});
if (useInternalVector)
{
buffer.resize ((size_t) samplesPerBlock, {});
bufferData = buffer.data();
}
smoother.reset (sampleRate, rampLengthInSeconds);

if (parameterHandle != nullptr)
Expand Down Expand Up @@ -74,6 +84,13 @@ void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::setRampLength (double
isCurrentlySmoothing = false;
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (int numSamples, ArenaAllocator& alloc)
{
bufferData = alloc.allocate<FloatType> (numSamples, bufferAlignment);
process (numSamples);
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (int numSamples)
{
Expand All @@ -95,13 +112,19 @@ void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (int numSample
}
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (FloatType value, int numSamples, ArenaAllocator& alloc)
{
bufferData = alloc.allocate<FloatType> (numSamples, bufferAlignment);
process (value, numSamples);
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (FloatType value, int numSamples)
{
const auto mappedValue = mappingFunction (value);
smoother.setTargetValue (mappedValue);

auto* bufferData = buffer.data();
if (! smoother.isSmoothing())
{
isCurrentlySmoothing = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ class SmoothedBufferValue
*/
void setParameterHandle (const FloatParameter* handle);

/** Prepare the smoother to process samples with a given sample rate and block size. */
void prepare (double sampleRate, int samplesPerBlock);
/**
* Prepare the smoother to process samples with a given sample rate
* and block size.
*
* If you're planning to use the SmoothedBuffer with an arena allocator,
* set useInternalVector to false.
*/
void prepare (double sampleRate, int samplesPerBlock, bool useInternalVector = true);

/** Resets the state of the smoother with a given value. */
void reset (FloatType resetValue);
Expand All @@ -69,18 +75,20 @@ class SmoothedBufferValue

/**
* Process smoothing for the current parameter handle.
* Please don't call this function if the parameter handle has nt been set!
* Please don't call this function if the parameter handle hasn't been set!
*/
void process (int numSamples);
void process (int numSamples, ArenaAllocator& alloc);

/**
* Process smoothing for the input value.
* If smoothing an audio parameter, it is recommended to use a parameter handle instead!
*/
void process (FloatType value, int numSamples);
void process (FloatType value, int numSamples, ArenaAllocator& alloc);

/** Returns a pointer to the current smoothed buffer. */
[[nodiscard]] const FloatType* getSmoothedBuffer() const { return buffer.data(); }
[[nodiscard]] const FloatType* getSmoothedBuffer() const { return bufferData; }

/**
* Optional mapping function to map from the set value to the smoothed value.
Expand All @@ -97,6 +105,8 @@ class SmoothedBufferValue
#else
std::vector<FloatType> buffer;
#endif
FloatType* bufferData = nullptr;

juce::SmoothedValue<FloatType, ValueSmoothingType> smoother;
bool isCurrentlySmoothing = false;

Expand Down

0 comments on commit 4482306

Please sign in to comment.