Just to add it here, I had some fun trying to build something generic & fully templated that does not need a lambda to get an operation for all channels call done with a one liner
template <typename ...Args>
struct ForAllChans
{
template <void(*op)(float*, const float*, Args..., int), typename SampleType>
static void call (AudioBuffer<SampleType>& buffer, Args ...args)
{
auto numChans = buffer.getNumChannels();
auto numSamps = buffer.getNumSamples();
auto** bufPtr = buffer.getArrayOfWritePointers();
for (int c = 0; c < numChans; ++c)
op (bufPtr[c], const_cast<const SampleType*>(bufPtr[c]), args..., numSamps);
}
};
Usage:
ForAllChans<float, float>::call<FloatVectorOperations::clip> (someBuffer, -1.0f, 1.0f);
The variadic Args...
template parameter denotes the argument types between the pointers and the number of samples that the specific float vector op needs. This makes the line above a bit ugly to read
Any C++ expert here knows a way this could be deducted from the static function call?
Edit: I
Stackoverflow (at least sometimes)
Some clever person over there helped me to get it done better. Solution now:
template <typename T>
struct TypeIdentityHolder { using type = T; };
template <typename T>
using TypeID = typename TypeIdentityHolder<T>::type;
template <typename SampleType, typename... Args>
void forAllChans (void (*op)(SampleType*, const SampleType*, TypeID<Args>..., int), AudioBuffer<SampleType>& buffer, Args... args)
{
auto numChans = buffer.getNumChannels();
auto numSamps = buffer.getNumSamples();
auto** bufPtr = buffer.getArrayOfWritePointers();
for (int c = 0; c < numChans; ++c)
op (bufPtr[c], const_cast<const SampleType*>(bufPtr[c]), args..., numSamps);
}
Usage:
forAllChans (FloatVectorOperations::clip, someBuffer, -1.0f, 1.0f);