SSE work
This commit is contained in:
parent
88a5f0d0d7
commit
834a907c2f
20 changed files with 669 additions and 931 deletions
|
|
@ -13,47 +13,65 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
|||
add_link_options(-stdlib=libc++) # New command on CMake master, not in 3.12 release
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
# CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
# if(COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
# endif()
|
||||
if (UNIX)
|
||||
add_compile_options(-Wall)
|
||||
add_compile_options(-Wextra)
|
||||
# add_compile_options(-fno-exceptions)
|
||||
add_compile_options(-ffast-math)
|
||||
add_compile_options(-fno-rtti)
|
||||
add_compile_options(-fno-omit-frame-pointer)
|
||||
endif()
|
||||
|
||||
# CHECK_CXX_COMPILER_FLAG("-ffast-math" COMPILER_SUPPORTS_FASTMATH)
|
||||
# if(COMPILER_SUPPORTS_FASTMATH)
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math")
|
||||
# endif()
|
||||
message("Cmake flags: ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# Check SIMD files
|
||||
include(CheckIncludeFiles)
|
||||
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
|
||||
CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_H)
|
||||
if (HAVE_X86INTRIN_H)
|
||||
if(UNIX)
|
||||
add_compile_options(-DHAVE_X86INTRIN_H)
|
||||
endif()
|
||||
endif()
|
||||
if (HAVE_ARM_NEON_H)
|
||||
if(UNIX)
|
||||
add_compile_options(-DHAVE_ARM_NEON_H)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build options and includes
|
||||
set(BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests")
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable Google Benchmark tests")
|
||||
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Disable Google Benchmark install")
|
||||
add_subdirectory(external/abseil-cpp)
|
||||
add_subdirectory(external/spdlog)
|
||||
add_subdirectory(external/Catch2)
|
||||
add_subdirectory(external/cxxopts)
|
||||
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable Google Benchmark tests")
|
||||
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Disable Google Benchmark install")
|
||||
add_subdirectory(external/benchmark)
|
||||
|
||||
# Export the compile_commands.json file
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
||||
set(COMMON_SOURCES
|
||||
sources/Opcode.cpp
|
||||
sources/Synth.cpp
|
||||
sources/FilePool.cpp
|
||||
sources/Region.cpp
|
||||
sources/StereoBuffer.cpp
|
||||
sources/Parser.cpp
|
||||
)
|
||||
|
||||
set(TEST_SOURCES
|
||||
tests/Regex.cpp
|
||||
tests/Helpers.cpp
|
||||
tests/Region.cpp
|
||||
tests/Range.cpp
|
||||
tests/Opcode.cpp
|
||||
tests/Buffer.cpp
|
||||
tests/AudioBuffer.cpp
|
||||
tests/Files.cpp
|
||||
tests/Main.cpp
|
||||
tests/RegexT.cpp
|
||||
tests/HelpersT.cpp
|
||||
tests/RegionT.cpp
|
||||
tests/RangeT.cpp
|
||||
tests/OpcodeT.cpp
|
||||
tests/BufferT.cpp
|
||||
tests/StereoBufferT.cpp
|
||||
tests/FilesT.cpp
|
||||
tests/MainT.cpp
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -87,17 +105,17 @@ target_include_directories(sfizz_tests SYSTEM PRIVATE sources)
|
|||
file(COPY "tests" DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
###############################
|
||||
add_executable(bench_ab_fill benchmarks/AudioBuffer_Fill.cpp ${COMMON_SOURCES})
|
||||
add_executable(bench_fill benchmarks/Stereo_Fill.cpp ${COMMON_SOURCES})
|
||||
# Per OS properties
|
||||
if(UNIX)
|
||||
target_link_libraries(bench_ab_fill stdc++fs)
|
||||
target_link_libraries(bench_fill stdc++fs)
|
||||
endif(UNIX)
|
||||
target_link_libraries(bench_ab_fill absl::strings benchmark)
|
||||
target_link_libraries(bench_fill absl::strings benchmark)
|
||||
|
||||
###############################
|
||||
add_executable(bench_ab_read benchmarks/AudioBuffer_Interleaved_Read.cpp ${COMMON_SOURCES})
|
||||
add_executable(bench_read benchmarks/Stereo_Read_Interleaved.cpp ${COMMON_SOURCES})
|
||||
# Per OS properties
|
||||
if(UNIX)
|
||||
target_link_libraries(bench_ab_read stdc++fs)
|
||||
target_link_libraries(bench_read stdc++fs)
|
||||
endif(UNIX)
|
||||
target_link_libraries(bench_ab_read absl::strings benchmark)
|
||||
target_link_libraries(bench_read absl::strings benchmark)
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include "../sources/AudioBuffer.h"
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
static void Joint_Fill_float(benchmark::State& state) {
|
||||
AudioBuffer<float> buffer (100001);
|
||||
float fillValue = 0.0f;
|
||||
for (auto _ : state) {
|
||||
buffer.fill(fillValue);
|
||||
fillValue += 1.0f;
|
||||
}
|
||||
}
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK(Joint_Fill_float);
|
||||
|
||||
static void Split_Fill_float(benchmark::State& state) {
|
||||
SplitAudioBuffer<float> buffer (100001);
|
||||
float fillValue = 0.0f;
|
||||
for (auto _ : state) {
|
||||
buffer.fill(fillValue);
|
||||
fillValue += 1.0f;
|
||||
}
|
||||
}
|
||||
BENCHMARK(Split_Fill_float);
|
||||
|
||||
static void Joint_Fill_float_SSE(benchmark::State& state) {
|
||||
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;
|
||||
for (auto _ : state) {
|
||||
buffer.fill(fillValue);
|
||||
fillValue += 1.0;
|
||||
}
|
||||
}
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK(Joint_Fill_double);
|
||||
|
||||
static void Split_Fill_double(benchmark::State& state) {
|
||||
SplitAudioBuffer<double> buffer (100001);
|
||||
double fillValue = 0.0;
|
||||
for (auto _ : state) {
|
||||
buffer.fill(fillValue);
|
||||
fillValue += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(Split_Fill_double);
|
||||
BENCHMARK_MAIN();
|
||||
39
benchmarks/Stereo_Fill.cpp
Normal file
39
benchmarks/Stereo_Fill.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include "../sources/StereoBuffer.h"
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
static void Fill_float(benchmark::State& state) {
|
||||
StereoBuffer<float> buffer (100001);
|
||||
float fillValue = 0.0f;
|
||||
for (auto _ : state) {
|
||||
buffer.fill(fillValue);
|
||||
fillValue += 1.0f;
|
||||
}
|
||||
}
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK(Fill_float);
|
||||
|
||||
static void Fill_float_SSE(benchmark::State& state) {
|
||||
StereoBuffer<float> buffer (100001);
|
||||
float fillValue = 0.0f;
|
||||
for (auto _ : state) {
|
||||
buffer.fill<SIMD::sse>(fillValue);
|
||||
fillValue += 1.0f;
|
||||
}
|
||||
}
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK(Fill_float_SSE);
|
||||
|
||||
static void Fill_double(benchmark::State& state) {
|
||||
StereoBuffer<double> buffer (100001);
|
||||
double fillValue = 0.0;
|
||||
for (auto _ : state) {
|
||||
buffer.fill(fillValue);
|
||||
fillValue += 1.0;
|
||||
}
|
||||
}
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK(Fill_double);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include "../sources/AudioBuffer.h"
|
||||
#include "../sources/StereoBuffer.h"
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
static void Interleaved_Read(benchmark::State& state) {
|
||||
AudioBuffer<float> buffer (state.range(0));
|
||||
StereoBuffer<float> buffer (state.range(0));
|
||||
std::vector<float> input (state.range(0) * 2);
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
|
|
@ -14,21 +14,21 @@ static void Interleaved_Read(benchmark::State& state) {
|
|||
BENCHMARK(Interleaved_Read)->Range((8<<10) + 3, (8<<20) + 3);
|
||||
|
||||
static void Interleaved_Read_SSE(benchmark::State& state) {
|
||||
AudioBuffer<float> buffer (state.range(0));
|
||||
StereoBuffer<float> buffer (state.range(0));
|
||||
std::vector<float> input (state.range(0) * 2);
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
buffer.readInterleaved<VectorOperations::sse>(input.data(), state.range(0));
|
||||
buffer.readInterleaved<SIMD::sse>(input.data(), state.range(0));
|
||||
}
|
||||
}
|
||||
BENCHMARK(Interleaved_Read_SSE)->Range((8<<10) + 3, (8<<20) + 3);
|
||||
|
||||
static void Unaligned_Interleaved_Read_SSE(benchmark::State& state) {
|
||||
AudioBuffer<float> buffer (state.range(0));
|
||||
StereoBuffer<float> buffer (state.range(0));
|
||||
std::vector<float> input (state.range(0) * 2);
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
for (auto _ : state) {
|
||||
buffer.readInterleaved<VectorOperations::sse>(input.data() + 1, state.range(0) - 1);
|
||||
buffer.readInterleaved<SIMD::sse>(input.data() + 1, state.range(0) - 1);
|
||||
}
|
||||
}
|
||||
BENCHMARK(Unaligned_Interleaved_Read_SSE)->Range((8<<10) + 3, (8<<20) + 3);
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
#pragma once
|
||||
#include "Buffer.h"
|
||||
#include "Helpers.h"
|
||||
#include "Globals.h"
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
template<class Type, unsigned int NumChannels = sfz::config::numChannels, unsigned int Alignment = config::defaultAlignment>
|
||||
|
||||
class AudioBuffer
|
||||
{
|
||||
public:
|
||||
AudioBuffer() = default;
|
||||
AudioBuffer(int numFrames)
|
||||
{
|
||||
DBG("Building an audiobuffer of size " << numFrames);
|
||||
resize(numFrames);
|
||||
}
|
||||
|
||||
bool resize(int numFrames)
|
||||
{
|
||||
// should have a positive number of frames...
|
||||
ASSERT(numFrames >= 0);
|
||||
padding = TypeAlignment - (numFrames & TypeAlignmentMask);
|
||||
totalSize = NumChannels * (numFrames + padding);
|
||||
if (buffer.resize(static_cast<size_t>(totalSize)))
|
||||
{
|
||||
this->numFrames = numFrames;
|
||||
for (auto channelIndex = 0; channelIndex < NumChannels; ++channelIndex)
|
||||
channels[channelIndex] = buffer.data() + channelIndex * (numFrames + padding);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Type& getSample(int channelIndex, int sampleIndex) noexcept
|
||||
{
|
||||
ASSERT(channelIndex >= 0);
|
||||
ASSERT(sampleIndex >= 0);
|
||||
return *(channels[channelIndex] + sampleIndex);
|
||||
}
|
||||
|
||||
template<VectorOperations op = VectorOperations::standard>
|
||||
void fill(Type value) noexcept
|
||||
{
|
||||
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
|
||||
{
|
||||
for(auto i = 0; i < NumChannels; ++i)
|
||||
std::fill(begin(i), end(i), value);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const auto input0 = _mm_loadu_ps(in);
|
||||
in += 4;
|
||||
const auto input1 = _mm_loadu_ps(in);
|
||||
in += 4;
|
||||
const auto intermediate0 = _mm_unpacklo_ps(input0, input1);
|
||||
const auto intermediate1 = _mm_unpackhi_ps(input0, input1);
|
||||
const auto output0 = _mm_unpacklo_ps(intermediate0, intermediate1);
|
||||
const 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
|
||||
{
|
||||
return channels[channelIndex];
|
||||
}
|
||||
|
||||
Type* begin(int channelIndex) noexcept
|
||||
{
|
||||
return channels[channelIndex];
|
||||
}
|
||||
|
||||
Type* end(int channelIndex) noexcept
|
||||
{
|
||||
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
|
||||
{
|
||||
return getSample(channelIndex, 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 };
|
||||
std::array<Type*, NumChannels> channels;
|
||||
Buffer<Type, Alignment> buffer {};
|
||||
Buffer<Type, Alignment> tempBuffer {2 * Alignment};
|
||||
};
|
||||
|
||||
template<class Type, unsigned int NumChannels = sfz::config::numChannels, unsigned int Alignment = config::defaultAlignment>
|
||||
class SplitAudioBuffer
|
||||
{
|
||||
public:
|
||||
SplitAudioBuffer() = default;
|
||||
SplitAudioBuffer(int numFrames)
|
||||
{
|
||||
resize(numFrames);
|
||||
}
|
||||
|
||||
bool resize(int numFrames)
|
||||
{
|
||||
// should have a positive number of frames...
|
||||
ASSERT(numFrames >= 0);
|
||||
bool resizedOK = true;
|
||||
|
||||
for (auto& buffer: buffers)
|
||||
resizedOK &= buffer.resize(static_cast<size_t>(numFrames));
|
||||
|
||||
if (resizedOK)
|
||||
this->numFrames = numFrames;
|
||||
else
|
||||
this->numFrames = std::min(numFrames, this->numFrames);
|
||||
|
||||
return resizedOK;
|
||||
}
|
||||
|
||||
template<VectorOperations op = VectorOperations::standard>
|
||||
void fill(Type value) noexcept
|
||||
{
|
||||
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
|
||||
{
|
||||
for(auto i = 0; i < NumChannels; ++i)
|
||||
std::fill(begin(i), end(i), value);
|
||||
}
|
||||
}
|
||||
|
||||
Type& getSample(int channelIndex, int sampleIndex) noexcept
|
||||
{
|
||||
ASSERT(channelIndex >= 0);
|
||||
ASSERT(sampleIndex >= 0);
|
||||
return *(buffers[channelIndex].data() + sampleIndex);
|
||||
}
|
||||
|
||||
Type& operator()(int channelIndex, int sampleIndex) noexcept
|
||||
{
|
||||
return getSample(channelIndex, sampleIndex);
|
||||
}
|
||||
|
||||
Type* getChannel(int channelIndex) noexcept
|
||||
{
|
||||
return buffers[channelIndex].data();
|
||||
}
|
||||
|
||||
Type* begin(int channelIndex) noexcept
|
||||
{
|
||||
return buffers[channelIndex].begin();
|
||||
}
|
||||
|
||||
Type* end(int channelIndex) noexcept
|
||||
{
|
||||
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 getNumChannels() const noexcept { return NumChannels; }
|
||||
bool empty() const noexcept { return numFrames == 0; }
|
||||
private:
|
||||
int numFrames { 0 };
|
||||
std::array<Buffer<Type, Alignment>, NumChannels> buffers;
|
||||
};
|
||||
|
|
@ -7,9 +7,6 @@
|
|||
template<class Type, unsigned int Alignment = config::defaultAlignment>
|
||||
class Buffer
|
||||
{
|
||||
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 constexpr auto AlignmentMask { Alignment - 1 };
|
||||
public:
|
||||
Buffer() { }
|
||||
Buffer(size_t size)
|
||||
|
|
@ -25,42 +22,52 @@ public:
|
|||
}
|
||||
|
||||
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 = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(Type)) : std::malloc(tempSize * sizeof(Type));
|
||||
if (newData == nullptr)
|
||||
return false;
|
||||
|
||||
largerSize = tempSize;
|
||||
alignedSize = newSize;
|
||||
largerData = static_cast<Type*>(newData);
|
||||
alignedData = static_cast<Type*>(std::align(Alignment, alignedSize, newData, tempSize));
|
||||
paddedData = static_cast<Type*>(newData);
|
||||
normalData = static_cast<Type*>(std::align(Alignment, alignedSize, newData, tempSize));
|
||||
normalEnd = normalData + alignedSize;
|
||||
if (auto endMisalignment = (alignedSize & TypeAlignmentMask); endMisalignment != 0)
|
||||
_alignedEnd = normalEnd + Alignment - endMisalignment;
|
||||
return true;
|
||||
}
|
||||
|
||||
Type* data() { return alignedData; }
|
||||
Type* data() { return normalData; }
|
||||
void clear()
|
||||
{
|
||||
std::free(largerData);
|
||||
largerSize = 0;
|
||||
alignedSize = 0;
|
||||
alignedData = nullptr;
|
||||
std::free(paddedData);
|
||||
normalData = nullptr;
|
||||
normalEnd = nullptr;
|
||||
_alignedEnd = nullptr;
|
||||
}
|
||||
~Buffer()
|
||||
{
|
||||
std::free(largerData);
|
||||
std::free(paddedData);
|
||||
}
|
||||
|
||||
Type& operator[](int idx) { return *(alignedData + idx); }
|
||||
Type& operator[](int idx) { return *(normalData + idx); }
|
||||
size_t size() const noexcept { return alignedSize; }
|
||||
bool empty() const noexcept { return alignedSize == 0; }
|
||||
|
||||
Type* begin() noexcept { return data(); }
|
||||
Type* end() noexcept { return data() + alignedSize; }
|
||||
Type* alignedEnd() noexcept { return data() + alignedSize; }
|
||||
// const Type* cbegin() const noexcept { return data(); }
|
||||
// const Type* cend() const noexcept { return data() + alignedSize; }
|
||||
Type* end() noexcept { return normalEnd; }
|
||||
Type* alignedEnd() noexcept { return _alignedEnd; }
|
||||
private:
|
||||
static constexpr auto AlignmentMask { Alignment - 1 };
|
||||
static constexpr auto TypeAlignment { Alignment / sizeof(Type) };
|
||||
static constexpr auto TypeAlignmentMask { TypeAlignment - 1 };
|
||||
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(TypeAlignment * sizeof(Type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
|
||||
size_t largerSize { 0 };
|
||||
size_t alignedSize { 0 };
|
||||
Type* largerData { nullptr };
|
||||
Type* alignedData { nullptr };
|
||||
Type* normalData { nullptr };
|
||||
Type* paddedData { nullptr };
|
||||
Type* normalEnd { nullptr };
|
||||
Type* _alignedEnd { nullptr };
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#pragma once
|
||||
#include "AudioBuffer.h"
|
||||
#include "StereoBuffer.h"
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
|
|
@ -12,6 +12,6 @@ public:
|
|||
FilePool() = default;
|
||||
private:
|
||||
std::filesystem::path rootDirectory;
|
||||
std::map<std::string_view, std::shared_ptr<AudioBuffer<float>>> preloadedData;
|
||||
std::map<std::string_view, std::shared_ptr<StereoBuffer<float>>> preloadedData;
|
||||
};
|
||||
}
|
||||
|
|
@ -20,12 +20,13 @@ namespace config
|
|||
|
||||
} // namespace sfz
|
||||
|
||||
enum class VectorOperations { standard, sse, neon };
|
||||
enum class SIMD { scalar, sse, neon };
|
||||
namespace config
|
||||
{
|
||||
inline constexpr unsigned int defaultAlignment { 16 };
|
||||
|
||||
inline constexpr VectorOperations vectorOperation { VectorOperations::standard };
|
||||
inline constexpr SIMD vectorOperation { SIMD::scalar };
|
||||
} // namespace config
|
||||
|
||||
#include <x86intrin.h>
|
||||
#if HAVE_X86INTRIN_H
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
58
sources/StereoBuffer.cpp
Normal file
58
sources/StereoBuffer.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#include "StereoBuffer.h"
|
||||
|
||||
template<>
|
||||
template<>
|
||||
void StereoBuffer<float>::readInterleaved<SIMD::sse>(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<>
|
||||
template<>
|
||||
void StereoBuffer<float, 16>::fill<SIMD::sse>(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<float*>(mmLeft), mmValue);
|
||||
_mm_store_ps(reinterpret_cast<float*>(mmRight), mmValue);
|
||||
mmLeft++;
|
||||
mmRight++;
|
||||
}
|
||||
}
|
||||
136
sources/StereoBuffer.h
Normal file
136
sources/StereoBuffer.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#pragma once
|
||||
#include "Buffer.h"
|
||||
#include "Helpers.h"
|
||||
#include "Globals.h"
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
enum class Channel {left, right};
|
||||
|
||||
template<class Type, unsigned int Alignment = config::defaultAlignment>
|
||||
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<size_t>(numFrames)) && rightBuffer.resize(static_cast<size_t>(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<SIMD op = SIMD::scalar>
|
||||
void fill(Type value) noexcept
|
||||
{
|
||||
std::fill(begin(Channel::left), end(Channel::left), value);
|
||||
std::fill(begin(Channel::right), end(Channel::right), value);
|
||||
}
|
||||
|
||||
template<SIMD op = SIMD::scalar>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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<Type*, Type*> getChannels() noexcept { return { leftBuffer.data(), rightBuffer.data() }; }
|
||||
std::pair<Type*, Type*> 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<Type*, Type*> 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<Type*, Type*> 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<Type, Alignment> leftBuffer {};
|
||||
Buffer<Type, Alignment> rightBuffer {};
|
||||
Type trash { 0 };
|
||||
};
|
||||
|
|
@ -1,524 +0,0 @@
|
|||
#include "catch2/catch.hpp"
|
||||
#include "../sources/AudioBuffer.h"
|
||||
#include <algorithm>
|
||||
using namespace Catch::literals;
|
||||
|
||||
TEST_CASE("[AudioBuffer/SplitBuffer] Empty buffers")
|
||||
{
|
||||
AudioBuffer<float> floatBuffer;
|
||||
REQUIRE(floatBuffer.empty());
|
||||
REQUIRE(floatBuffer.getNumFrames() == 0);
|
||||
AudioBuffer<double> doubleBuffer;
|
||||
REQUIRE(doubleBuffer.empty());
|
||||
REQUIRE(doubleBuffer.getNumFrames() == 0);
|
||||
AudioBuffer<int> intBuffer;
|
||||
REQUIRE(intBuffer.empty());
|
||||
REQUIRE(intBuffer.getNumFrames() == 0);
|
||||
|
||||
SplitAudioBuffer<float> floatSplitBuffer;
|
||||
REQUIRE(floatSplitBuffer.empty());
|
||||
REQUIRE(floatSplitBuffer.getNumFrames() == 0);
|
||||
SplitAudioBuffer<double> doubleSplitBuffer;
|
||||
REQUIRE(doubleSplitBuffer.empty());
|
||||
REQUIRE(doubleSplitBuffer.getNumFrames() == 0);
|
||||
SplitAudioBuffer<int> intSplitBuffer;
|
||||
REQUIRE(intSplitBuffer.empty());
|
||||
REQUIRE(intSplitBuffer.getNumFrames() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer/SplitBuffer] Non-empty")
|
||||
{
|
||||
AudioBuffer<float> floatBuffer(10);
|
||||
REQUIRE(!floatBuffer.empty());
|
||||
REQUIRE(floatBuffer.getNumFrames() == 10);
|
||||
AudioBuffer<double> doubleBuffer(10);
|
||||
REQUIRE(!doubleBuffer.empty());
|
||||
REQUIRE(doubleBuffer.getNumFrames() == 10);
|
||||
AudioBuffer<int> intBuffer(10);
|
||||
REQUIRE(!intBuffer.empty());
|
||||
REQUIRE(intBuffer.getNumFrames() == 10);
|
||||
|
||||
SplitAudioBuffer<float> floatSplitBuffer(10);
|
||||
REQUIRE(!floatSplitBuffer.empty());
|
||||
REQUIRE(floatSplitBuffer.getNumFrames() == 10);
|
||||
SplitAudioBuffer<double> doubleSplitBuffer(10);
|
||||
REQUIRE(!doubleSplitBuffer.empty());
|
||||
REQUIRE(doubleSplitBuffer.getNumFrames() == 10);
|
||||
SplitAudioBuffer<int> intSplitBuffer(10);
|
||||
REQUIRE(!intSplitBuffer.empty());
|
||||
REQUIRE(intSplitBuffer.getNumFrames() == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer/SplitBuffer] Access")
|
||||
{
|
||||
const int size { 5 };
|
||||
AudioBuffer<double> doubleBuffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < doubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx)
|
||||
doubleBuffer.getSample(chanIdx, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) * chanIdx + frameIdx;
|
||||
|
||||
for (auto chanIdx = 0; chanIdx < doubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx)
|
||||
REQUIRE(doubleBuffer.getSample(chanIdx, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) * chanIdx + frameIdx);
|
||||
|
||||
SplitAudioBuffer<double> splitDoubleBuffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < splitDoubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < splitDoubleBuffer.getNumFrames(); ++frameIdx)
|
||||
splitDoubleBuffer.getSample(chanIdx, frameIdx) = static_cast<double>(splitDoubleBuffer.getNumFrames()) * chanIdx + frameIdx;
|
||||
|
||||
for (auto chanIdx = 0; chanIdx < splitDoubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < splitDoubleBuffer.getNumFrames(); ++frameIdx)
|
||||
REQUIRE(splitDoubleBuffer.getSample(chanIdx, frameIdx) == static_cast<double>(splitDoubleBuffer.getNumFrames()) * chanIdx + frameIdx);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer/SplitBuffer] Access 2")
|
||||
{
|
||||
const int size { 5 };
|
||||
AudioBuffer<int> doubleBuffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < doubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx)
|
||||
doubleBuffer(chanIdx, frameIdx) = static_cast<int>(doubleBuffer.getNumFrames()) * chanIdx + frameIdx;
|
||||
|
||||
for (auto chanIdx = 0; chanIdx < doubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx)
|
||||
REQUIRE(doubleBuffer(chanIdx, frameIdx) == static_cast<int>(doubleBuffer.getNumFrames()) * chanIdx + frameIdx);
|
||||
|
||||
SplitAudioBuffer<int> splitDoubleBuffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < splitDoubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < splitDoubleBuffer.getNumFrames(); ++frameIdx)
|
||||
splitDoubleBuffer(chanIdx, frameIdx) = static_cast<int>(splitDoubleBuffer.getNumFrames()) * chanIdx + frameIdx;
|
||||
|
||||
for (auto chanIdx = 0; chanIdx < splitDoubleBuffer.getNumChannels(); ++chanIdx)
|
||||
for (auto frameIdx = 0; frameIdx < splitDoubleBuffer.getNumFrames(); ++frameIdx)
|
||||
REQUIRE(splitDoubleBuffer(chanIdx, frameIdx) == static_cast<int>(splitDoubleBuffer.getNumFrames()) * chanIdx + frameIdx);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer/SplitBuffer] Iterators")
|
||||
{
|
||||
const int size { 256 };
|
||||
const float fillValue { 2.0f };
|
||||
AudioBuffer<float> buffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
|
||||
std::fill(buffer.begin(chanIdx), buffer.end(chanIdx), fillValue);
|
||||
|
||||
for (auto chanIdx = 0; chanIdx < buffer.getNumChannels(); ++chanIdx)
|
||||
REQUIRE( std::all_of(buffer.begin(chanIdx), buffer.end(chanIdx), [fillValue](auto value) { return value == fillValue; }) );
|
||||
|
||||
SplitAudioBuffer<float> splitBuffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < splitBuffer.getNumChannels(); ++chanIdx)
|
||||
std::fill(splitBuffer.begin(chanIdx), splitBuffer.end(chanIdx), fillValue);
|
||||
|
||||
for (auto chanIdx = 0; chanIdx < splitBuffer.getNumChannels(); ++chanIdx)
|
||||
REQUIRE( std::all_of(splitBuffer.begin(chanIdx), splitBuffer.end(chanIdx), [fillValue](auto value) { return value == fillValue; }) );
|
||||
}
|
||||
|
||||
template<class Type, unsigned int NumChannels, unsigned int Alignment = 16>
|
||||
void channelAlignmentTest(int size)
|
||||
{
|
||||
static constexpr auto AlignmentMask { Alignment - 1 };
|
||||
const Type fillValue { 2 };
|
||||
AudioBuffer<Type, NumChannels, Alignment> buffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < NumChannels; ++chanIdx)
|
||||
REQUIRE( ((size_t)buffer.getChannel(chanIdx) & AlignmentMask) == 0 );
|
||||
|
||||
SplitAudioBuffer<Type, NumChannels, Alignment> splitBuffer(size);
|
||||
for (auto chanIdx = 0; chanIdx < NumChannels; ++chanIdx)
|
||||
REQUIRE( ((size_t)splitBuffer.getChannel(chanIdx) & AlignmentMask) == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer/SplitBuffer] Channel alignments (floats)")
|
||||
{
|
||||
channelAlignmentTest<float, 1>(4);
|
||||
channelAlignmentTest<float, 1>(5);
|
||||
channelAlignmentTest<float, 1>(8);
|
||||
channelAlignmentTest<float, 1>(256);
|
||||
channelAlignmentTest<float, 1>(257);
|
||||
channelAlignmentTest<float, 1>(1023);
|
||||
channelAlignmentTest<float, 1>(1024);
|
||||
channelAlignmentTest<float, 1>(65537);
|
||||
channelAlignmentTest<float, 1>(65536);
|
||||
channelAlignmentTest<float, 1>(65535);
|
||||
|
||||
channelAlignmentTest<float, 2>(4);
|
||||
channelAlignmentTest<float, 2>(5);
|
||||
channelAlignmentTest<float, 2>(8);
|
||||
channelAlignmentTest<float, 2>(256);
|
||||
channelAlignmentTest<float, 2>(257);
|
||||
channelAlignmentTest<float, 2>(1023);
|
||||
channelAlignmentTest<float, 2>(1024);
|
||||
channelAlignmentTest<float, 2>(65537);
|
||||
channelAlignmentTest<float, 2>(65536);
|
||||
channelAlignmentTest<float, 2>(65535);
|
||||
|
||||
channelAlignmentTest<float, 3>(4);
|
||||
channelAlignmentTest<float, 3>(5);
|
||||
channelAlignmentTest<float, 3>(8);
|
||||
channelAlignmentTest<float, 3>(256);
|
||||
channelAlignmentTest<float, 3>(257);
|
||||
channelAlignmentTest<float, 3>(1023);
|
||||
channelAlignmentTest<float, 3>(1024);
|
||||
channelAlignmentTest<float, 3>(65537);
|
||||
channelAlignmentTest<float, 3>(65536);
|
||||
channelAlignmentTest<float, 3>(65535);
|
||||
|
||||
channelAlignmentTest<float, 4>(4);
|
||||
channelAlignmentTest<float, 4>(5);
|
||||
channelAlignmentTest<float, 4>(8);
|
||||
channelAlignmentTest<float, 4>(256);
|
||||
channelAlignmentTest<float, 4>(257);
|
||||
channelAlignmentTest<float, 4>(1023);
|
||||
channelAlignmentTest<float, 4>(1024);
|
||||
channelAlignmentTest<float, 4>(65537);
|
||||
channelAlignmentTest<float, 4>(65536);
|
||||
channelAlignmentTest<float, 4>(65535);
|
||||
|
||||
channelAlignmentTest<float, 1, 4>(4);
|
||||
channelAlignmentTest<float, 1, 4>(5);
|
||||
channelAlignmentTest<float, 1, 4>(8);
|
||||
channelAlignmentTest<float, 1, 4>(256);
|
||||
channelAlignmentTest<float, 1, 4>(257);
|
||||
channelAlignmentTest<float, 1, 4>(1023);
|
||||
channelAlignmentTest<float, 1, 4>(1024);
|
||||
channelAlignmentTest<float, 1, 4>(65537);
|
||||
channelAlignmentTest<float, 1, 4>(65536);
|
||||
channelAlignmentTest<float, 1, 4>(65535);
|
||||
|
||||
channelAlignmentTest<float, 2, 4>(4);
|
||||
channelAlignmentTest<float, 2, 4>(5);
|
||||
channelAlignmentTest<float, 2, 4>(8);
|
||||
channelAlignmentTest<float, 2, 4>(256);
|
||||
channelAlignmentTest<float, 2, 4>(257);
|
||||
channelAlignmentTest<float, 2, 4>(1023);
|
||||
channelAlignmentTest<float, 2, 4>(1024);
|
||||
channelAlignmentTest<float, 2, 4>(65537);
|
||||
channelAlignmentTest<float, 2, 4>(65536);
|
||||
channelAlignmentTest<float, 2, 4>(65535);
|
||||
|
||||
channelAlignmentTest<float, 1, 8>(4);
|
||||
channelAlignmentTest<float, 1, 8>(5);
|
||||
channelAlignmentTest<float, 1, 8>(8);
|
||||
channelAlignmentTest<float, 1, 8>(256);
|
||||
channelAlignmentTest<float, 1, 8>(257);
|
||||
channelAlignmentTest<float, 1, 8>(1023);
|
||||
channelAlignmentTest<float, 1, 8>(1024);
|
||||
channelAlignmentTest<float, 1, 8>(65537);
|
||||
channelAlignmentTest<float, 1, 8>(65536);
|
||||
channelAlignmentTest<float, 1, 8>(65535);
|
||||
|
||||
channelAlignmentTest<float, 2, 8>(4);
|
||||
channelAlignmentTest<float, 2, 8>(5);
|
||||
channelAlignmentTest<float, 2, 8>(8);
|
||||
channelAlignmentTest<float, 2, 8>(256);
|
||||
channelAlignmentTest<float, 2, 8>(257);
|
||||
channelAlignmentTest<float, 2, 8>(1023);
|
||||
channelAlignmentTest<float, 2, 8>(1024);
|
||||
channelAlignmentTest<float, 2, 8>(65537);
|
||||
channelAlignmentTest<float, 2, 8>(65536);
|
||||
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 );
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer] Fill a big Audiobuffer")
|
||||
{
|
||||
constexpr int size { 2039247 };
|
||||
AudioBuffer<float> buffer (size);
|
||||
std::vector<float> input (2*size);
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
buffer.readInterleaved(input.data(), size);
|
||||
}
|
||||
|
|
@ -31,18 +31,29 @@ TEST_CASE("[Buffer] Empty (uint8_t)")
|
|||
REQUIRE(emptyBuffer.size() == 0);
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
void checkBoundaries(Buffer<Type>& buffer, size_t expectedSize)
|
||||
{
|
||||
REQUIRE(buffer.size() == expectedSize);
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
REQUIRE(((size_t)buffer.alignedEnd() & (config::defaultAlignment - 1)) == 0);
|
||||
REQUIRE(std::distance(buffer.begin(), buffer.end()) == expectedSize);
|
||||
REQUIRE(std::distance(buffer.begin(), buffer.alignedEnd()) >= expectedSize);
|
||||
}
|
||||
|
||||
TEST_CASE("[Buffer] 10 floats ")
|
||||
{
|
||||
Buffer<float> buffer(10);
|
||||
REQUIRE(!buffer.empty());
|
||||
REQUIRE(buffer.size() == 10);
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
const int baseSize { 10 };
|
||||
Buffer<float> buffer(baseSize);
|
||||
checkBoundaries(buffer, baseSize);
|
||||
|
||||
for (auto& element: buffer)
|
||||
element = 0.0f;
|
||||
for (auto& element: buffer)
|
||||
REQUIRE(element == 0.0f);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("[Buffer] Resize 10 floats ")
|
||||
{
|
||||
const int baseSize { 10 };
|
||||
|
|
@ -50,37 +61,39 @@ TEST_CASE("[Buffer] Resize 10 floats ")
|
|||
const int bigSize { baseSize * 2 };
|
||||
Buffer<float> buffer(baseSize);
|
||||
REQUIRE(!buffer.empty());
|
||||
REQUIRE(buffer.size() == baseSize);
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, baseSize);
|
||||
|
||||
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
||||
|
||||
REQUIRE( buffer.resize(smallSize) );
|
||||
REQUIRE( buffer.size() == smallSize );
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, smallSize);
|
||||
|
||||
REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) );
|
||||
|
||||
REQUIRE( buffer.resize(bigSize) );
|
||||
REQUIRE( buffer.size() == bigSize );
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, bigSize);
|
||||
for (auto i = 0; i < smallSize; ++i)
|
||||
REQUIRE(buffer[i] == 1.0f);
|
||||
}
|
||||
|
||||
TEST_CASE("[Buffer] Resize 4096 floats ")
|
||||
{
|
||||
const int baseSize { 10 };
|
||||
const int baseSize { 4096 };
|
||||
const int smallSize { baseSize / 2 };
|
||||
const int bigSize { baseSize * 2 };
|
||||
Buffer<float> buffer(baseSize);
|
||||
REQUIRE(!buffer.empty());
|
||||
REQUIRE(buffer.size() == baseSize);
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, baseSize);
|
||||
|
||||
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
||||
|
||||
REQUIRE( buffer.resize(smallSize) );
|
||||
REQUIRE( buffer.size() == smallSize );
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, smallSize);
|
||||
|
||||
REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) );
|
||||
|
||||
REQUIRE( buffer.resize(bigSize) );
|
||||
REQUIRE( buffer.size() == bigSize );
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, bigSize);
|
||||
for (auto i = 0; i < smallSize; ++i)
|
||||
REQUIRE(buffer[i] == 1.0f);
|
||||
}
|
||||
|
|
@ -90,18 +103,19 @@ TEST_CASE("[Buffer] Resize 65536 floats ")
|
|||
const int baseSize { 10 };
|
||||
const int smallSize { baseSize / 2 };
|
||||
const int bigSize { baseSize * 2 };
|
||||
Buffer<float> buffer(baseSize);
|
||||
Buffer<float> buffer(baseSize);
|
||||
REQUIRE(!buffer.empty());
|
||||
REQUIRE(buffer.size() == baseSize);
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, baseSize);
|
||||
|
||||
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
||||
|
||||
REQUIRE( buffer.resize(smallSize) );
|
||||
REQUIRE( buffer.size() == smallSize );
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, smallSize);
|
||||
|
||||
REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) );
|
||||
|
||||
REQUIRE( buffer.resize(bigSize) );
|
||||
REQUIRE( buffer.size() == bigSize );
|
||||
REQUIRE(((size_t)buffer.data() & (config::defaultAlignment - 1)) == 0);
|
||||
checkBoundaries(buffer, bigSize);
|
||||
for (auto i = 0; i < smallSize; ++i)
|
||||
REQUIRE(buffer[i] == 1.0f);
|
||||
}
|
||||
315
tests/StereoBufferT.cpp
Normal file
315
tests/StereoBufferT.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
#include "catch2/catch.hpp"
|
||||
#include "../sources/StereoBuffer.h"
|
||||
#include <algorithm>
|
||||
using namespace Catch::literals;
|
||||
|
||||
TEST_CASE("[StereoBuffer] Empty buffers")
|
||||
{
|
||||
StereoBuffer<float> floatBuffer;
|
||||
REQUIRE(floatBuffer.empty());
|
||||
REQUIRE(floatBuffer.getNumFrames() == 0);
|
||||
StereoBuffer<double> doubleBuffer;
|
||||
REQUIRE(doubleBuffer.empty());
|
||||
REQUIRE(doubleBuffer.getNumFrames() == 0);
|
||||
StereoBuffer<int> intBuffer;
|
||||
REQUIRE(intBuffer.empty());
|
||||
REQUIRE(intBuffer.getNumFrames() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Non-empty")
|
||||
{
|
||||
StereoBuffer<float> floatBuffer(10);
|
||||
REQUIRE(!floatBuffer.empty());
|
||||
REQUIRE(floatBuffer.getNumFrames() == 10);
|
||||
StereoBuffer<double> doubleBuffer(10);
|
||||
REQUIRE(!doubleBuffer.empty());
|
||||
REQUIRE(doubleBuffer.getNumFrames() == 10);
|
||||
StereoBuffer<int> intBuffer(10);
|
||||
REQUIRE(!intBuffer.empty());
|
||||
REQUIRE(intBuffer.getNumFrames() == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Access")
|
||||
{
|
||||
const int size { 5 };
|
||||
StereoBuffer<double> doubleBuffer(size);
|
||||
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx)
|
||||
{
|
||||
doubleBuffer.getSample(Channel::left, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx;
|
||||
doubleBuffer.getSample(Channel::right, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx;
|
||||
}
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx)
|
||||
{
|
||||
REQUIRE(doubleBuffer.getSample(Channel::left, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx);
|
||||
REQUIRE(doubleBuffer(Channel::left, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx);
|
||||
REQUIRE(doubleBuffer.getSample(Channel::right, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx);
|
||||
REQUIRE(doubleBuffer(Channel::right, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Iterators")
|
||||
{
|
||||
const int size { 256 };
|
||||
const float fillValue { 2.0f };
|
||||
StereoBuffer<float> buffer(size);
|
||||
std::fill(buffer.begin(Channel::left), buffer.end(Channel::left), fillValue);
|
||||
std::fill(buffer.begin(Channel::right), buffer.end(Channel::right), fillValue);
|
||||
|
||||
REQUIRE( std::all_of(buffer.begin(Channel::left), buffer.end(Channel::left), [fillValue](auto value) { return value == fillValue; }) );
|
||||
REQUIRE( std::all_of(buffer.begin(Channel::right), buffer.end(Channel::right), [fillValue](auto value) { return value == fillValue; }) );
|
||||
}
|
||||
|
||||
template<class Type, unsigned int Alignment = 16>
|
||||
void channelAlignmentTest(int size)
|
||||
{
|
||||
static constexpr auto AlignmentMask { Alignment - 1 };
|
||||
StereoBuffer<Type, Alignment> buffer(size);
|
||||
REQUIRE( ((size_t)buffer.getChannel(Channel::left) & AlignmentMask) == 0 );
|
||||
REQUIRE( ((size_t)buffer.getChannel(Channel::right) & AlignmentMask) == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Channel alignments (floats)")
|
||||
{
|
||||
channelAlignmentTest<float>(4);
|
||||
channelAlignmentTest<float>(5);
|
||||
channelAlignmentTest<float>(8);
|
||||
channelAlignmentTest<float>(256);
|
||||
channelAlignmentTest<float>(257);
|
||||
channelAlignmentTest<float>(1023);
|
||||
channelAlignmentTest<float>(1024);
|
||||
channelAlignmentTest<float>(65537);
|
||||
channelAlignmentTest<float>(65536);
|
||||
channelAlignmentTest<float>(65535);
|
||||
|
||||
channelAlignmentTest<float, 4>(4);
|
||||
channelAlignmentTest<float, 4>(5);
|
||||
channelAlignmentTest<float, 4>(8);
|
||||
channelAlignmentTest<float, 4>(256);
|
||||
channelAlignmentTest<float, 4>(257);
|
||||
channelAlignmentTest<float, 4>(1023);
|
||||
channelAlignmentTest<float, 4>(1024);
|
||||
channelAlignmentTest<float, 4>(65537);
|
||||
channelAlignmentTest<float, 4>(65536);
|
||||
channelAlignmentTest<float, 4>(65535);
|
||||
|
||||
channelAlignmentTest<float, 8>(4);
|
||||
channelAlignmentTest<float, 8>(5);
|
||||
channelAlignmentTest<float, 8>(8);
|
||||
channelAlignmentTest<float, 8>(256);
|
||||
channelAlignmentTest<float, 8>(257);
|
||||
channelAlignmentTest<float, 8>(1023);
|
||||
channelAlignmentTest<float, 8>(1024);
|
||||
channelAlignmentTest<float, 8>(65537);
|
||||
channelAlignmentTest<float, 8>(65536);
|
||||
channelAlignmentTest<float, 8>(65535);
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Channel alignments (doubles)")
|
||||
{
|
||||
channelAlignmentTest<double>(4);
|
||||
channelAlignmentTest<double>(5);
|
||||
channelAlignmentTest<double>(8);
|
||||
channelAlignmentTest<double>(256);
|
||||
channelAlignmentTest<double>(257);
|
||||
channelAlignmentTest<double>(1023);
|
||||
channelAlignmentTest<double>(1024);
|
||||
channelAlignmentTest<double>(65537);
|
||||
channelAlignmentTest<double>(65536);
|
||||
channelAlignmentTest<double>(65535);
|
||||
|
||||
channelAlignmentTest<double, 8>(4);
|
||||
channelAlignmentTest<double, 8>(5);
|
||||
channelAlignmentTest<double, 8>(8);
|
||||
channelAlignmentTest<double, 8>(256);
|
||||
channelAlignmentTest<double, 8>(257);
|
||||
channelAlignmentTest<double, 8>(1023);
|
||||
channelAlignmentTest<double, 8>(1024);
|
||||
channelAlignmentTest<double, 8>(65537);
|
||||
channelAlignmentTest<double, 8>(65536);
|
||||
channelAlignmentTest<double, 8>(65535);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer] fills")
|
||||
{
|
||||
SECTION("Floats - 0.0")
|
||||
{
|
||||
StereoBuffer<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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::left, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
SECTION("Floats - 1.0")
|
||||
{
|
||||
StereoBuffer<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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::left, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
SECTION("Doubles - 0.0")
|
||||
{
|
||||
StereoBuffer<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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::left, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
SECTION("Doubles - 1.0")
|
||||
{
|
||||
StereoBuffer<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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::left, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
SECTION("Floats - 0.0 - SSE")
|
||||
{
|
||||
StereoBuffer<float> buffer(10);
|
||||
buffer.fill<SIMD::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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::left, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
SECTION("Floats - 1.0 - SSE")
|
||||
{
|
||||
StereoBuffer<float> buffer(10);
|
||||
buffer.fill<SIMD::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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::left, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[frameIdx] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Interleave read")
|
||||
{
|
||||
StereoBuffer<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 frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[realIdx++] = buffer(Channel::left, frameIdx);
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[realIdx++] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Interleave read -- SSE")
|
||||
{
|
||||
StereoBuffer<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<SIMD::sse>(input.data(), 8);
|
||||
std::array<float, 16> real { 0.0f };
|
||||
auto realIdx = 0;
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[realIdx++] = buffer(Channel::left, frameIdx);
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[realIdx++] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] Interleave read unaligned end -- SSE")
|
||||
{
|
||||
StereoBuffer<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<SIMD::sse>(input.data(), 10);
|
||||
std::array<float, 20> real { 0.0f };
|
||||
auto realIdx = 0;
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[realIdx++] = buffer(Channel::left, frameIdx);
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
real[realIdx++] = buffer(Channel::right, frameIdx);
|
||||
REQUIRE( real == expected );
|
||||
}
|
||||
|
||||
TEST_CASE("[StereoBuffer] SSE vs scalar")
|
||||
{
|
||||
constexpr int numFrames { 91 };
|
||||
StereoBuffer<float> buffer(numFrames);
|
||||
std::array<float, numFrames * 2> input;
|
||||
std::iota(input.begin(), input.end(), 0.0f);
|
||||
StereoBuffer<float> expectedBuffer (numFrames);
|
||||
expectedBuffer.readInterleaved(input.data(), 10);
|
||||
buffer.readInterleaved<SIMD::sse>(input.data(), 10);
|
||||
std::array<float, numFrames * 2> expectedArray { 0.0f };
|
||||
std::array<float, numFrames * 2> realArray { 0.0f };
|
||||
auto sampleIdx = 0;
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
{
|
||||
expectedArray[sampleIdx] = buffer(Channel::left, frameIdx);
|
||||
realArray[sampleIdx++] = buffer(Channel::left, frameIdx);
|
||||
}
|
||||
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
|
||||
{
|
||||
expectedArray[sampleIdx] = buffer(Channel::right, frameIdx);
|
||||
realArray[sampleIdx++] = buffer(Channel::right, frameIdx);
|
||||
}
|
||||
REQUIRE( realArray == expectedArray );
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioBuffer] Fill a big Audiobuffer")
|
||||
{
|
||||
constexpr int size { 2039247 };
|
||||
StereoBuffer<float> buffer (size);
|
||||
std::vector<float> input (2*size);
|
||||
std::iota(input.begin(), input.end(), 1.0f);
|
||||
buffer.readInterleaved(input.data(), size);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue