#pragma once #include "Buffer.h" #include "Helpers.h" #include "Globals.h" #include #include #include enum class Channel {left, right}; template class StereoBuffer { public: static constexpr int numChannels { 2 }; StereoBuffer() = default; StereoBuffer(int numFrames) { resize(numFrames); } bool resize(int numFrames) { // should have a positive number of frames... ASSERT(numFrames >= 0); if (leftBuffer.resize(static_cast(numFrames)) && rightBuffer.resize(static_cast(numFrames))) { this->numFrames = numFrames; return true; } return false; } Type& getSample(Channel channel, int sampleIndex) noexcept { ASSERT(sampleIndex >= 0); switch(channel) { case Channel::left: return leftBuffer[sampleIndex]; case Channel::right: return rightBuffer[sampleIndex]; // Should not be here by construction... default: ASSERTFALSE; return trash; } } template void fill(Type value) noexcept { std::fill(begin(Channel::left), end(Channel::left), value); std::fill(begin(Channel::right), end(Channel::right), value); } template void readInterleaved(Type* input, int numFrames) noexcept { auto* in = input; auto* end = input + numChannels * numFrames; auto [lOut, rOut] = getChannels(); while (in < end) { *lOut = *in; in++; *rOut = *in; in++; lOut += 1; rOut += 1; } } template <> void readInterleaved(float *input, int numFrames) noexcept { ASSERT(this->numFrames >= numFrames); const int residualFrames = numFrames & (2 * TypeAlignment - 1); const int lastAligned = numFrames - residualFrames; float *in = input; auto [lOut, rOut] = getChannels(); const float *end = input + 2 * lastAligned; while (in < end) { auto register0 = _mm_loadu_ps(in); in += 4; auto register1 = _mm_loadu_ps(in); in += 4; auto register2 = register0; // register 2 holds the copy of register 0 that is going to get erased by the first operation // Remember that the bit mask reads from the end; 10 00 10 00 means // "take 0 from a, take 2 from a, take 0 from b, take 2 from b" register0 = _mm_shuffle_ps(register0, register1, 0b10001000); register1 = _mm_shuffle_ps(register2, register1, 0b11011101); _mm_store_ps(lOut, register0); _mm_store_ps(rOut, register1); lOut += 4; rOut += 4; } end = input + numChannels * numFrames; while (in < end) { *lOut = *in; in++; *rOut = *in; in++; lOut += 1; rOut += 1; } } template<> void fill(float value) noexcept { const __m128 mmValue = _mm_set_ps1(value); auto [lBegin, rBegin] = getChannels(); auto [lEnd, rEnd] = alignedEnds(); auto mmLeft = reinterpret_cast<__m128 *>(lBegin); auto mmRight = reinterpret_cast<__m128 *>(rBegin); while (mmLeft < reinterpret_cast<__m128 *>(lEnd)) // we should only need to test a single channel { _mm_store_ps(reinterpret_cast(mmLeft), mmValue); _mm_store_ps(reinterpret_cast(mmRight), mmValue); mmLeft++; mmRight++; } } Type* getChannel(Channel channel) noexcept { switch(channel) { case Channel::left: return leftBuffer.data(); case Channel::right: return rightBuffer.data(); default: return {}; } } Type* begin(Channel channel) noexcept { switch(channel) { case Channel::left: return leftBuffer.data(); case Channel::right: return rightBuffer.data(); default: return {}; } } std::pair getChannels() noexcept { return { leftBuffer.data(), rightBuffer.data() }; } std::pair begins() noexcept { return { leftBuffer.data(), rightBuffer.data() }; } Type* end(Channel channel) noexcept { switch(channel) { case Channel::left: return leftBuffer.end(); case Channel::right: return rightBuffer.end(); default: return {}; } } std::pair ends() { return { leftBuffer.end(), rightBuffer.end() }; } Type* alignedEnd(Channel channel) noexcept { switch(channel) { case Channel::left: return leftBuffer.alignedEnd(); case Channel::right: return rightBuffer.alignedEnd(); default: return {}; } } std::pair alignedEnds() { return { leftBuffer.alignedEnd(), rightBuffer.alignedEnd() }; } Type& operator()(Channel channel, int sampleIndex) noexcept { return getSample(channel, sampleIndex); } int getNumFrames() const noexcept { return numFrames; } int getNumChannels() const noexcept { return numChannels; } bool empty() const noexcept { return numFrames == 0; } private: static constexpr auto TypeAlignment { Alignment / sizeof(Type) }; static constexpr auto TypeAlignmentMask { TypeAlignment - 1 }; static_assert(TypeAlignment * sizeof(Type) == Alignment, "The alignment does not appear to be divided by the size of the Type"); int numFrames { 0 }; int totalSize { 0 }; int padding { 0 }; Buffer leftBuffer {}; Buffer rightBuffer {}; Type trash { 0 }; };