WIP on vector operations

This commit is contained in:
Paul Ferrand 2019-07-31 01:45:58 +02:00
parent bb95d8ed86
commit c014a34385
5 changed files with 460 additions and 21 deletions

View file

@ -1,9 +1,10 @@
#include <benchmark/benchmark.h> #include <benchmark/benchmark.h>
#include "../sources/AudioBuffer.h" #include "../sources/AudioBuffer.h"
#include <algorithm> #include <algorithm>
#include <numeric>
static void AB_Joint_Fill_float(benchmark::State& state) { static void Joint_Fill_float(benchmark::State& state) {
AudioBuffer<float> buffer (65535); AudioBuffer<float> buffer (100001);
float fillValue = 0.0f; float fillValue = 0.0f;
for (auto _ : state) { for (auto _ : state) {
buffer.fill(fillValue); buffer.fill(fillValue);
@ -11,20 +12,43 @@ static void AB_Joint_Fill_float(benchmark::State& state) {
} }
} }
// Register the function as a benchmark // Register the function as a benchmark
BENCHMARK(AB_Joint_Fill_float); BENCHMARK(Joint_Fill_float);
static void AB_Split_Fill_float(benchmark::State& state) { static void Split_Fill_float(benchmark::State& state) {
SplitAudioBuffer<float> buffer (65535); SplitAudioBuffer<float> buffer (100001);
float fillValue = 0.0f; float fillValue = 0.0f;
for (auto _ : state) { for (auto _ : state) {
buffer.fill(fillValue); buffer.fill(fillValue);
fillValue += 1.0f; fillValue += 1.0f;
} }
} }
BENCHMARK(AB_Split_Fill_float); BENCHMARK(Split_Fill_float);
static void AB_Joint_Fill_double(benchmark::State& state) { static void Joint_Fill_float_SSE(benchmark::State& state) {
AudioBuffer<double> buffer (65535); AudioBuffer<float> buffer (100001);
float fillValue = 0.0f;
for (auto _ : state) {
buffer.fill<VectorOperations::sse>(fillValue);
fillValue += 1.0f;
}
}
// Register the function as a benchmark
BENCHMARK(Joint_Fill_float_SSE);
static void Split_Fill_float_SSE(benchmark::State& state) {
SplitAudioBuffer<float> buffer (100001);
float fillValue = 0.0f;
for (auto _ : state) {
buffer.fill<VectorOperations::sse>(fillValue);
fillValue += 1.0f;
}
}
BENCHMARK(Split_Fill_float_SSE);
static void Joint_Fill_double(benchmark::State& state) {
AudioBuffer<double> buffer (100001);
double fillValue = 0.0; double fillValue = 0.0;
for (auto _ : state) { for (auto _ : state) {
buffer.fill(fillValue); buffer.fill(fillValue);
@ -32,15 +56,37 @@ static void AB_Joint_Fill_double(benchmark::State& state) {
} }
} }
// Register the function as a benchmark // Register the function as a benchmark
BENCHMARK(AB_Joint_Fill_double); BENCHMARK(Joint_Fill_double);
static void AB_Split_Fill_double(benchmark::State& state) { static void Split_Fill_double(benchmark::State& state) {
SplitAudioBuffer<double> buffer (65535); SplitAudioBuffer<double> buffer (100001);
double fillValue = 0.0; double fillValue = 0.0;
for (auto _ : state) { for (auto _ : state) {
buffer.fill(fillValue); buffer.fill(fillValue);
fillValue += 1.0; fillValue += 1.0;
} }
} }
BENCHMARK(AB_Split_Fill_double);
BENCHMARK(Split_Fill_double);
constexpr int size { 1039247 };
static void Interleaved_Read(benchmark::State& state) {
AudioBuffer<float> buffer (size);
std::array<float, 2*size> input;
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.readInterleaved(input.data(), size);
}
}
BENCHMARK(Interleaved_Read);
static void Interleaved_Read_SSE(benchmark::State& state) {
AudioBuffer<float> buffer (size);
std::array<float, 2*size> input;
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.readInterleaved<VectorOperations::sse>(input.data(), size);
}
}
BENCHMARK(Interleaved_Read_SSE);
BENCHMARK_MAIN(); BENCHMARK_MAIN();

View file

@ -2,6 +2,8 @@
#include "Buffer.h" #include "Buffer.h"
#include "Helpers.h" #include "Helpers.h"
#include "Globals.h" #include "Globals.h"
#include <array>
#include <type_traits>
template<class Type, unsigned int NumChannels = sfz::config::numChannels, unsigned int Alignment = config::defaultAlignment> template<class Type, unsigned int NumChannels = sfz::config::numChannels, unsigned int Alignment = config::defaultAlignment>
@ -35,14 +37,19 @@ public:
{ {
ASSERT(channelIndex >= 0); ASSERT(channelIndex >= 0);
ASSERT(sampleIndex >= 0); ASSERT(sampleIndex >= 0);
return *(buffer.data() + numFrames * channelIndex + sampleIndex); return *(channels[channelIndex] + sampleIndex);
} }
template<VectorOperations op = VectorOperations::standard>
void fill(Type value) noexcept void fill(Type value) noexcept
{ {
if constexpr (config::vectorOperation == config::VectorOperations::sse) if constexpr (op == VectorOperations::sse && std::is_same<Type, float>::value)
{ {
static_assert(Alignment == 16, "Wrong alignment");
const __m128 mmValue = _mm_set_ps1(value);
for(auto i = 0; i < NumChannels; ++i)
for (auto mm = (__m128*)alignedBegin(i); mm < (__m128*)alignedEnd(i); mm++)
_mm_store_ps((float*)mm, mmValue);
} }
else else
{ {
@ -51,6 +58,65 @@ public:
} }
} }
template<VectorOperations op = VectorOperations::standard>
void readInterleaved(float* input, int numFrames) noexcept
{
ASSERT(this->numFrames >= numFrames);
if constexpr (op == VectorOperations::sse && std::is_same<Type, float>::value && NumChannels == 2)
{
static_assert(Alignment == 16, "Wrong alignment");
const int residualFrames = numFrames & (2 * TypeAlignment - 1);
const int lastAligned = numFrames - residualFrames;
float* in = input;
float* out0 = getChannel(0);
float* out1 = getChannel(1);
const float* end = input + 2 * lastAligned;
while (in < end)
{
auto input0 = _mm_load_ps(in);
in += 4;
auto input1 = _mm_load_ps(in);
in += 4;
auto intermediate0 = _mm_unpacklo_ps(input0, input1);
auto intermediate1 = _mm_unpackhi_ps(input0, input1);
auto output0 = _mm_unpacklo_ps(intermediate0, intermediate1);
auto output1 = _mm_unpackhi_ps(intermediate0, intermediate1);
_mm_store_ps(out0, output0);
_mm_store_ps(out1, output1);
out0 += 4;
out1 += 4;
}
for (auto chanIdx = 0; chanIdx < NumChannels; chanIdx++)
{
auto* _in = input + 2 * lastAligned + chanIdx;
auto* _end = input + numFrames * NumChannels;
auto* _out = getChannel(chanIdx) + lastAligned;
while (_in < _end)
{
*_out = *_in;
_in += 2;
_out += 1;
}
}
}
else
{
for (auto chanIdx = 0; chanIdx < NumChannels; chanIdx++)
{
auto* _in = input + chanIdx;
auto* _end = input + numFrames * NumChannels;
auto* _out = getChannel(chanIdx);
while (_in < _end)
{
*_out = *_in;
_in += NumChannels;
_out += 1;
}
}
}
}
Type* getChannel(int channelIndex) noexcept Type* getChannel(int channelIndex) noexcept
{ {
return channels[channelIndex]; return channels[channelIndex];
@ -58,12 +124,22 @@ public:
Type* begin(int channelIndex) noexcept Type* begin(int channelIndex) noexcept
{ {
return buffer.data() + numFrames * channelIndex; return channels[channelIndex];
} }
Type* end(int channelIndex) noexcept Type* end(int channelIndex) noexcept
{ {
return buffer.data() + numFrames * (channelIndex + 1); return buffer.data() + numFrames * (channelIndex + 1) + padding * channelIndex;
}
Type* alignedBegin(int channelIndex) noexcept
{
return begin(channelIndex);
}
Type* alignedEnd(int channelIndex) noexcept
{
return buffer.data() + (channelIndex + 1) * (numFrames + padding);
} }
Type& operator()(int channelIndex, int sampleIndex) noexcept Type& operator()(int channelIndex, int sampleIndex) noexcept
@ -84,6 +160,7 @@ private:
int padding { 0 }; int padding { 0 };
std::array<Type*, NumChannels> channels; std::array<Type*, NumChannels> channels;
Buffer<Type, Alignment> buffer {}; Buffer<Type, Alignment> buffer {};
Buffer<Type, Alignment> tempBuffer {2 * Alignment};
}; };
template<class Type, unsigned int NumChannels = sfz::config::numChannels, unsigned int Alignment = config::defaultAlignment> template<class Type, unsigned int NumChannels = sfz::config::numChannels, unsigned int Alignment = config::defaultAlignment>
@ -113,11 +190,16 @@ public:
return resizedOK; return resizedOK;
} }
template<VectorOperations op = VectorOperations::standard>
void fill(Type value) noexcept void fill(Type value) noexcept
{ {
if constexpr (config::vectorOperation == config::VectorOperations::sse) if constexpr (op == VectorOperations::sse)
{ {
static_assert(Alignment == 16, "Wrong alignment");
const __m128 mmValue = _mm_set_ps1(value);
for(auto i = 0; i < NumChannels; ++i)
for (auto mm = (__m128*)alignedBegin(i); mm < (__m128*)alignedEnd(i); mm++)
_mm_store_ps((float*)mm, mmValue);
} }
else else
{ {
@ -153,6 +235,16 @@ public:
return buffers[channelIndex].end(); return buffers[channelIndex].end();
} }
Type* alignedBegin(int channelIndex) noexcept
{
return begin(channelIndex);
}
Type* alignedEnd(int channelIndex) noexcept
{
return buffers[channelIndex].alignedEnd();
}
int getNumFrames() const noexcept { return numFrames; } int getNumFrames() const noexcept { return numFrames; }
int getNumChannels() const noexcept { return NumChannels; } int getNumChannels() const noexcept { return NumChannels; }
bool empty() const noexcept { return numFrames == 0; } bool empty() const noexcept { return numFrames == 0; }

View file

@ -9,6 +9,7 @@ class Buffer
{ {
static_assert(std::is_arithmetic<Type>::value, "Type should be arithmetic"); static_assert(std::is_arithmetic<Type>::value, "Type should be arithmetic");
static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value"); static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value");
static constexpr auto AlignmentMask { Alignment - 1 };
public: public:
Buffer() { } Buffer() { }
Buffer(size_t size) Buffer(size_t size)
@ -23,7 +24,7 @@ public:
return true; return true;
} }
auto tempSize = newSize + Alignment; auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end
auto* newData = largerData != nullptr ? std::realloc(largerData, tempSize * sizeof(Type)) : std::malloc(tempSize * sizeof(Type)); auto* newData = largerData != nullptr ? std::realloc(largerData, tempSize * sizeof(Type)) : std::malloc(tempSize * sizeof(Type));
if (newData == nullptr) if (newData == nullptr)
return false; return false;
@ -54,6 +55,7 @@ public:
Type* begin() noexcept { return data(); } Type* begin() noexcept { return data(); }
Type* end() noexcept { return data() + alignedSize; } Type* end() noexcept { return data() + alignedSize; }
Type* alignedEnd() noexcept { return data() + alignedSize; }
// const Type* cbegin() const noexcept { return data(); } // const Type* cbegin() const noexcept { return data(); }
// const Type* cend() const noexcept { return data() + alignedSize; } // const Type* cend() const noexcept { return data() + alignedSize; }
private: private:

View file

@ -26,4 +26,6 @@ namespace config
inline constexpr unsigned int defaultAlignment { 16 }; inline constexpr unsigned int defaultAlignment { 16 };
inline constexpr VectorOperations vectorOperation { VectorOperations::standard }; inline constexpr VectorOperations vectorOperation { VectorOperations::standard };
} // namespace config } // namespace config
#include <x86intrin.h>

View file

@ -215,4 +215,301 @@ TEST_CASE("[AudioBuffer/SplitBuffer] Channel alignments (floats)")
channelAlignmentTest<float, 2, 8>(65537); channelAlignmentTest<float, 2, 8>(65537);
channelAlignmentTest<float, 2, 8>(65536); channelAlignmentTest<float, 2, 8>(65536);
channelAlignmentTest<float, 2, 8>(65535); channelAlignmentTest<float, 2, 8>(65535);
}
TEST_CASE("[AudioBuffer/SplitBuffer] Channel alignments (doubles)")
{
channelAlignmentTest<double, 1>(4);
channelAlignmentTest<double, 1>(5);
channelAlignmentTest<double, 1>(8);
channelAlignmentTest<double, 1>(256);
channelAlignmentTest<double, 1>(257);
channelAlignmentTest<double, 1>(1023);
channelAlignmentTest<double, 1>(1024);
channelAlignmentTest<double, 1>(65537);
channelAlignmentTest<double, 1>(65536);
channelAlignmentTest<double, 1>(65535);
channelAlignmentTest<double, 2>(4);
channelAlignmentTest<double, 2>(5);
channelAlignmentTest<double, 2>(8);
channelAlignmentTest<double, 2>(256);
channelAlignmentTest<double, 2>(257);
channelAlignmentTest<double, 2>(1023);
channelAlignmentTest<double, 2>(1024);
channelAlignmentTest<double, 2>(65537);
channelAlignmentTest<double, 2>(65536);
channelAlignmentTest<double, 2>(65535);
channelAlignmentTest<double, 3>(4);
channelAlignmentTest<double, 3>(5);
channelAlignmentTest<double, 3>(8);
channelAlignmentTest<double, 3>(256);
channelAlignmentTest<double, 3>(257);
channelAlignmentTest<double, 3>(1023);
channelAlignmentTest<double, 3>(1024);
channelAlignmentTest<double, 3>(65537);
channelAlignmentTest<double, 3>(65536);
channelAlignmentTest<double, 3>(65535);
channelAlignmentTest<double, 4>(4);
channelAlignmentTest<double, 4>(5);
channelAlignmentTest<double, 4>(8);
channelAlignmentTest<double, 4>(256);
channelAlignmentTest<double, 4>(257);
channelAlignmentTest<double, 4>(1023);
channelAlignmentTest<double, 4>(1024);
channelAlignmentTest<double, 4>(65537);
channelAlignmentTest<double, 4>(65536);
channelAlignmentTest<double, 4>(65535);
channelAlignmentTest<double, 1, 8>(4);
channelAlignmentTest<double, 1, 8>(5);
channelAlignmentTest<double, 1, 8>(8);
channelAlignmentTest<double, 1, 8>(256);
channelAlignmentTest<double, 1, 8>(257);
channelAlignmentTest<double, 1, 8>(1023);
channelAlignmentTest<double, 1, 8>(1024);
channelAlignmentTest<double, 1, 8>(65537);
channelAlignmentTest<double, 1, 8>(65536);
channelAlignmentTest<double, 1, 8>(65535);
channelAlignmentTest<double, 2, 8>(4);
channelAlignmentTest<double, 2, 8>(5);
channelAlignmentTest<double, 2, 8>(8);
channelAlignmentTest<double, 2, 8>(256);
channelAlignmentTest<double, 2, 8>(257);
channelAlignmentTest<double, 2, 8>(1023);
channelAlignmentTest<double, 2, 8>(1024);
channelAlignmentTest<double, 2, 8>(65537);
channelAlignmentTest<double, 2, 8>(65536);
channelAlignmentTest<double, 2, 8>(65535);
}
TEST_CASE("[AudioBuffer] fills")
{
SECTION("Floats - 0.0")
{
AudioBuffer<float> buffer(10);
buffer.fill(0.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Floats - 1.0")
{
AudioBuffer<float> buffer(10);
buffer.fill(1.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 1.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Doubles - 0.0")
{
AudioBuffer<double> buffer(10);
buffer.fill(0.0);
std::array<double, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0);
std::array<double, 10> real { 2.0 };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Doubles - 1.0")
{
AudioBuffer<double> buffer(10);
buffer.fill(1.0);
std::array<double, 10> expected;
std::fill(expected.begin(), expected.end(), 1.0);
std::array<double, 10> real { 2.0 };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Floats - 0.0 - SSE")
{
AudioBuffer<float> buffer(10);
buffer.fill<VectorOperations::sse>(0.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Floats - 1.0 - SSE")
{
AudioBuffer<float> buffer(10);
buffer.fill<VectorOperations::sse>(1.0f);
std::array<float, 10> expected { 1.0f };
std::fill(expected.begin(), expected.end(), 1.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
}
TEST_CASE("[SplitAudioBuffer] fills")
{
SECTION("Floats - 0.0")
{
SplitAudioBuffer<float> buffer(10);
buffer.fill(0.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Floats - 1.0")
{
SplitAudioBuffer<float> buffer(10);
buffer.fill(1.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 1.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Doubles - 0.0")
{
SplitAudioBuffer<double> buffer(10);
buffer.fill(0.0);
std::array<double, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0);
std::array<double, 10> real { 2.0 };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Doubles - 1.0")
{
SplitAudioBuffer<double> buffer(10);
buffer.fill(1.0);
std::array<double, 10> expected;
std::fill(expected.begin(), expected.end(), 1.0);
std::array<double, 10> real { 2.0 };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Floats - 0.0 - SSE")
{
SplitAudioBuffer<float> buffer(10);
buffer.fill<VectorOperations::sse>(0.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
SECTION("Floats - 1.0 - SSE")
{
SplitAudioBuffer<float> buffer(10);
buffer.fill<VectorOperations::sse>(1.0f);
std::array<float, 10> expected { 1.0f };
std::fill(expected.begin(), expected.end(), 1.0f);
std::array<float, 10> real { 2.0f };
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
{
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
}
}
TEST_CASE("[AudioBuffer] Interleave read")
{
AudioBuffer<float> buffer(8);
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
buffer.readInterleaved(input.data(), 8);
std::array<float, 16> real { 0.0f };
auto realIdx = 0;
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
TEST_CASE("[AudioBuffer] Interleave read -- SSE")
{
AudioBuffer<float> buffer(8);
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
buffer.readInterleaved<VectorOperations::sse>(input.data(), 8);
std::array<float, 16> real { 0.0f };
auto realIdx = 0;
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
}
TEST_CASE("[AudioBuffer] Interleave read unaligned end -- SSE")
{
AudioBuffer<float> buffer(10);
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f};
buffer.readInterleaved<VectorOperations::sse>(input.data(), 10);
std::array<float, 20> real { 0.0f };
auto realIdx = 0;
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(chanIdx, frameIdx);
REQUIRE( real == expected );
} }