diff options
Diffstat (limited to 'src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc')
-rw-r--r-- | src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc new file mode 100644 index 000000000..7fb8e83b5 --- /dev/null +++ b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc @@ -0,0 +1,711 @@ +/* +Copyright 2018 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS-IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "base/simd_utils.h" + +#include <algorithm> +#include <cmath> +#include <cstdint> +#include <vector> + +#include "third_party/googletest/googletest/include/gtest/gtest.h" +#include "base/audio_buffer.h" +#include "base/constants_and_types.h" + +namespace vraudio { + +namespace { + +// Input lengths (purposefully chosen not to be a multiple of SIMD_LENGTH). +const size_t kInputSize = 7; +const size_t kNumTestChannels = 3; +const size_t kNumQuadChannels = 4; + +// Length of deinterleaved and interleaved buffers. +const size_t kHalfSize = 21; +const size_t kFullSize = kHalfSize * 2; +const size_t kQuadSize = kHalfSize * 4; +const size_t kPentSize = kHalfSize * 5; + +// The int16 values for the deinterleaving test. +const int16_t kOne = 0x0001; +const int16_t kTwo = 0x0002; +const int16_t kThree = 0x0003; +const int16_t kFour = 0x0004; +const int16_t kMax = 0x7FFF; +const int16_t kMin = -0x7FFF; + +// Epsilon for conversion from int16_t back to float. +const float kFloatEpsilon = 1e-4f; +const int16_t kIntEpsilon = 1; + +// Intereleaved data. +const int16_t kInterleavedInput[kFullSize] = { + kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, + kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, + kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, + kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo}; + +// Corresponding values for float and 16 bit int. +const float kFloatInput[kInputSize] = {0.5f, -0.5f, 1.0f, -1.0f, + 1.0f, -1.0f, 0.0f}; +const int16_t kIntInput[kInputSize] = {0x4000, -0x4000, 0x7FFF, -0x7FFF, + 0x7FFF, -0x7FFF, 0}; + +TEST(SimdUtilsTest, IsAlignedTest) { + AudioBuffer aligned_audio_buffer(kNumMonoChannels, kInputSize); + const float* aligned_ptr = aligned_audio_buffer[0].begin(); + const float* unaligned_ptr = aligned_ptr + 1; + EXPECT_TRUE(IsAligned(aligned_ptr)); + EXPECT_FALSE(IsAligned(unaligned_ptr)); +} + +TEST(SimdUtilsTest, AddPointwiseTest) { + const float kResult = 3.0f; + AudioBuffer aligned_audio_buffer(kNumTestChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = 1.0f; + aligned_audio_buffer[1][i] = 2.0f; + } + AddPointwise(kInputSize, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0], &aligned_audio_buffer[2][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[2][i], kResult); + } +} + +TEST(SimdUtilsTest, AddPointwiseInPlaceTest) { + AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = static_cast<float>(i); + } + const size_t kRuns = 2; + for (size_t i = 0; i < kRuns; ++i) { + AddPointwise(kInputSize, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0], &aligned_audio_buffer[1][0]); + } + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], static_cast<float>(i * kRuns)); + } +} + +TEST(SimdUtilsTest, SubtractPointwiseTest) { + AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = static_cast<float>(i); + aligned_audio_buffer[1][i] = static_cast<float>(2 * i); + } + SubtractPointwise(kInputSize, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0], &aligned_audio_buffer[1][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], aligned_audio_buffer[0][i]); + } +} + +TEST(SimdUtilsTest, MultiplyPointwiseTest) { + AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = static_cast<float>(i); + aligned_audio_buffer[1][i] = static_cast<float>(i); + } + MultiplyPointwise(kInputSize, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0], &aligned_audio_buffer[1][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], + aligned_audio_buffer[0][i] * aligned_audio_buffer[0][i]); + } +} + +TEST(SimdUtilsTest, MultiplyAndAccumulatePointwiseTest) { + const float kInitialOutput = 1.0f; + AudioBuffer aligned_input_buffer(kNumStereoChannels, kInputSize); + aligned_input_buffer.Clear(); + AudioBuffer aligned_output_buffer(kNumMonoChannels, kInputSize); + aligned_output_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_input_buffer[0][i] = static_cast<float>(i); + aligned_input_buffer[1][i] = static_cast<float>(i); + aligned_output_buffer[0][i] = kInitialOutput; + } + MultiplyAndAccumulatePointwise(kInputSize, &aligned_input_buffer[0][0], + &aligned_input_buffer[1][0], + &aligned_output_buffer[0][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_output_buffer[0][i], + kInitialOutput + aligned_input_buffer[0][i] * + aligned_input_buffer[1][i]); + } +} + +TEST(SimdUtilsTest, ScalarMultiplyTest) { + AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = 1.0f; + aligned_audio_buffer[1][i] = 0.0f; + } + const float gain = 0.5f; + ScalarMultiply(kInputSize, gain, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], + aligned_audio_buffer[0][i] * gain); + } +} + +TEST(SimdUtilsTest, ScalarMultiplyAndAccumuateTest) { + const float kResult = 2.0f; + AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = 0.5f; + aligned_audio_buffer[1][i] = 1.0f; + } + const float gain = 2.0f; + ScalarMultiplyAndAccumulate(kInputSize, gain, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], kResult); + } +} + +TEST(SimdUtilsTest, SqrtTest) { + const std::vector<float> kNumbers{130.0f, 13.0f, 1.3f, + 0.13f, 0.013f, 0.0013f}; + AudioBuffer numbers(kNumMonoChannels, kNumbers.size()); + AudioBuffer approximate(kNumMonoChannels, kNumbers.size()); + numbers[0] = kNumbers; + const float kSqrtEpsilon = 2e-3f; + + Sqrt(kNumbers.size(), numbers[0].begin(), approximate[0].begin()); + + for (size_t i = 0; i < kNumbers.size(); ++i) { + const float actual = std::sqrt(kNumbers[i]); + EXPECT_LT(std::abs(actual - approximate[0][i]) / actual, kSqrtEpsilon); + } +} + +TEST(SimdUtilsTest, ReciprocalSqrtTest) { + const std::vector<float> kNumbers{130.0f, 13.0f, 1.3f, + 0.13f, 0.013f, 0.0013f}; + AudioBuffer numbers(kNumMonoChannels, kNumbers.size()); + AudioBuffer sqrt(kNumMonoChannels, kNumbers.size()); + AudioBuffer recip_sqrt(kNumMonoChannels, kNumbers.size()); + + Sqrt(kNumbers.size(), numbers[0].begin(), sqrt[0].begin()); + ReciprocalSqrt(kNumbers.size(), numbers[0].begin(), recip_sqrt[0].begin()); + + for (size_t i = 0; i < kNumbers.size(); ++i) { + EXPECT_FLOAT_EQ(1.0f / recip_sqrt[0][i], sqrt[0][i]); + } +} + +// Tests that the correct complex magnitudes are calculated for a range of +// complex numbers with both positive and negative imaginary part. +TEST(SimdUtilsTest, ApproxComplexMagnitudeTest) { + const size_t kFramesPerBuffer = 17; + // Check that we are correct to within 0.5% of each value. + const float kErrEpsilon = 5e-3f; + AudioBuffer complex_buffer(kNumMonoChannels, 2 * kFramesPerBuffer); + for (size_t i = 0; i < kFramesPerBuffer; ++i) { + const size_t j = 2 * i; + complex_buffer[0][j] = static_cast<float>(i); + complex_buffer[0][j + 1] = ((i % 2) ? -1.0f : 1.0f) * static_cast<float>(i); + } + AudioBuffer magnitude_buffer(kNumMonoChannels, kFramesPerBuffer); + + ApproxComplexMagnitude(kFramesPerBuffer, complex_buffer[0].begin(), + magnitude_buffer[0].begin()); + + for (size_t sample = 0; sample < kFramesPerBuffer; ++sample) { + const float expected = static_cast<float>(sample) * kSqrtTwo; + // Check its correct to within 0.5%. + EXPECT_NEAR(magnitude_buffer[0][sample], expected, kErrEpsilon * expected); + } +} + +// Tests that the ComplexInterleavedFormatFromMagnitudeAndSinCosPhase() method +// correctly recovers the frequency response from magnitude and phase. +TEST(SimdUtilsTest, ComplexInterleavedFormatFromMagnitudeAndSinCosPhaseTest) { + // The folowing vectors contain the inverse sines and cosines of the numbers + // 0 to 0.75 in steps of 0.05 (calculated in MATLAB). + const size_t kLength = 16; + AudioBuffer cos_vec(kNumMonoChannels, kLength); + cos_vec[0] = {1.5708f, 1.5208f, 1.4706f, 1.4202f, 1.3694f, 1.3181f, + 1.2661f, 1.2132f, 1.1593f, 1.1040f, 1.0472f, 0.9884f, + 0.9273f, 0.8632f, 0.7954f, 0.7227f}; + AudioBuffer sin_vec(kNumMonoChannels, kLength); + sin_vec[0] = {0.0000f, 0.0500f, 0.1002f, 0.1506f, 0.2014f, 0.2527f, + 0.3047f, 0.3576f, 0.4115f, 0.4668f, 0.5236f, 0.5824f, + 0.6435f, 0.7076f, 0.7754f, 0.8481f}; + const float kMagnitude = 10.0f; + AudioBuffer magnitude(kNumMonoChannels, kLength); + std::fill(magnitude[0].begin(), magnitude[0].end(), kMagnitude); + const size_t output_size = 2 * sin_vec.num_frames(); + AudioBuffer output(kNumMonoChannels, output_size); + output.Clear(); + + ComplexInterleavedFormatFromMagnitudeAndSinCosPhase( + output_size, &magnitude[0][0], &cos_vec[0][0], &sin_vec[0][0], + &output[0][0]); + + for (size_t i = 0, j = 0; i < output_size; i += 2, ++j) { + EXPECT_FLOAT_EQ(output[0][i], kMagnitude * cos_vec[0][j]); + EXPECT_FLOAT_EQ(output[0][i + 1], kMagnitude * sin_vec[0][j]); + } +} + +TEST(SimdUtilsTest, StereoMonoTest) { + const float kResult = 2.0f / std::sqrt(2.0f); + AudioBuffer aligned_audio_buffer(kNumTestChannels, kInputSize); + aligned_audio_buffer.Clear(); + for (size_t i = 0; i < kInputSize; ++i) { + aligned_audio_buffer[0][i] = 1.0f; + aligned_audio_buffer[1][i] = 1.0f; + } + MonoFromStereoSimd(kInputSize, &aligned_audio_buffer[0][0], + &aligned_audio_buffer[1][0], &aligned_audio_buffer[2][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[2][i], kResult); + } + // Perform inverse operation. + StereoFromMonoSimd(kInputSize, &aligned_audio_buffer[2][0], + &aligned_audio_buffer[0][0], &aligned_audio_buffer[1][0]); + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_FLOAT_EQ(aligned_audio_buffer[0][i], 1.0f); + EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], 1.0f); + } +} + +TEST(SimdUtilsTest, InterleaveAlignedInt16Test) { + AudioBuffer::AlignedInt16Vector interleaved(kFullSize); + AudioBuffer::AlignedInt16Vector channel_0(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_1(kHalfSize); + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = kOne; + channel_1[i] = kTwo; + } + + InterleaveStereo(kHalfSize, channel_0.data(), channel_1.data(), + interleaved.data()); + + for (size_t i = 0; i < kFullSize; ++i) { + const int16_t value = (i % 2 == 0) ? kOne : kTwo; + EXPECT_EQ(interleaved[i], value); + } +} + +TEST(SimdUtilsTest, InterleaveUnalignedInt16Test) { + AudioBuffer::AlignedInt16Vector interleaved(kFullSize); + AudioBuffer::AlignedInt16Vector channel_0(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_1(kHalfSize); + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = kOne; + channel_1[i] = kTwo; + } + + InterleaveStereo(kHalfSize, channel_0.data(), channel_1.data(), + interleaved.data()); + + for (size_t i = 0; i < kFullSize; ++i) { + const int16_t value = (i % 2 == 0) ? kOne : kTwo; + EXPECT_EQ(interleaved[i], value); + } +} + +TEST(SimdUtilsTest, DeinterleaveAlignedInt16Test) { + AudioBuffer::AlignedInt16Vector interleaved(kFullSize); + AudioBuffer::AlignedInt16Vector channel_0(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_1(kHalfSize); + + // Fill the aligned input buffer. + for (size_t i = 0; i < kFullSize; ++i) { + interleaved[i] = kInterleavedInput[i]; + } + + // Clear the output buffers. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = static_cast<int16_t>(0); + channel_1[i] = static_cast<int16_t>(0); + } + + // Test the case where input is aligned. + DeinterleaveStereo(kHalfSize, interleaved.data(), channel_0.data(), + channel_1.data()); + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_EQ(channel_0[i], kOne); + EXPECT_EQ(channel_1[i], kTwo); + } +} + +TEST(SimdUtilsTest, DeinterleaveUnalignedInt16Test) { + AudioBuffer::AlignedInt16Vector channel_0(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_1(kHalfSize); + + // Clear the output buffers. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = static_cast<int16_t>(0); + channel_1[i] = static_cast<int16_t>(0); + } + + // Test the case where input is unaligned. + DeinterleaveStereo(kHalfSize, kInterleavedInput, channel_0.data(), + channel_1.data()); + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_EQ(channel_0[i], kOne); + EXPECT_EQ(channel_1[i], kTwo); + } +} + +TEST(SimdUtilsTest, DeinterleaveAlignedInt16ConvertToFloatTest) { + AudioBuffer::AlignedInt16Vector interleaved(kFullSize); + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kFullSize; ++i) { + interleaved[i] = i % 2 ? kMin : kMax; + } + + // Clear the output buffers. + planar.Clear(); + + // Test the case where input is aligned. + DeinterleaveStereo(kHalfSize, interleaved.data(), channel_0.begin(), + channel_1.begin()); + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_NEAR(channel_0[i], 1.0f, kEpsilonFloat); + EXPECT_NEAR(channel_1[i], -1.0f, kEpsilonFloat); + } +} + +TEST(SimdUtilsTest, DeinterleaveUnalignedInt16ConvertToFloatTest) { + int16_t interleaved[kFullSize]; + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the unaligned input buffer. + for (size_t i = 0; i < kFullSize; ++i) { + interleaved[i] = i % 2 ? kMin : kMax; + } + + // Clear the output buffers. + planar.Clear(); + + // Test the case where input is unaligned. + DeinterleaveStereo(kHalfSize, interleaved, channel_0.begin(), + channel_1.begin()); + + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_NEAR(channel_0[i], 1.0f, kEpsilonFloat); + EXPECT_NEAR(channel_1[i], -1.0f, kEpsilonFloat); + } +} + +TEST(SimdUtilsTest, InterleaveAlignedFloatTest) { + AudioBuffer interleaved(kNumMonoChannels, kFullSize); + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = 1.0f; + channel_1[i] = 2.0f; + } + + InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(), + interleaved[0].begin()); + + for (size_t i = 0; i < kFullSize; ++i) { + const float value = (i % 2 == 0) ? 1.0f : 2.0f; + EXPECT_FLOAT_EQ(interleaved[0][i], value); + } +} + +TEST(SimdUtilsTest, InterleaveUnalignedFloatTest) { + float interleaved[kFullSize]; + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = 1.0f; + channel_1[i] = 2.0f; + } + + InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(), + interleaved); + + for (size_t i = 0; i < kFullSize; ++i) { + const float value = (i % 2 == 0) ? 1.0f : 2.0f; + EXPECT_FLOAT_EQ(interleaved[i], value); + } +} + +TEST(SimdUtilsTest, InterleaveAlignedFloatConvertToInt16Test) { + AudioBuffer::AlignedInt16Vector interleaved(kFullSize); + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = 1.0f; + channel_1[i] = -1.0f; + } + + InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(), + interleaved.data()); + + for (size_t i = 0; i < kFullSize; ++i) { + const int16_t value = i % 2 ? kMin : kMax; + EXPECT_NEAR(interleaved[i], value, kIntEpsilon); + } +} + +TEST(SimdUtilsTest, InterleaveUnalignedFloatConvertToInt16Test) { + int16_t interleaved[kFullSize]; + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = 1.0f; + channel_1[i] = -1.0f; + } + + InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(), + interleaved); + + for (size_t i = 0; i < kFullSize; ++i) { + const int16_t value = i % 2 ? kMin : kMax; + EXPECT_NEAR(interleaved[i], value, kIntEpsilon); + } +} + +TEST(SimdUtilsTest, DeinterleaveAlignedFloatTest) { + AudioBuffer interleaved(kNumMonoChannels, kFullSize); + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kFullSize; ++i) { + interleaved[0][i] = (i % 2 == 0) ? 1.0f : 2.0f; + } + + // Clear the output buffers. + planar.Clear(); + + // Test the case where input is aligned. + DeinterleaveStereo(kHalfSize, interleaved[0].begin(), channel_0.begin(), + channel_1.begin()); + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_FLOAT_EQ(channel_0[i], 1.0f); + EXPECT_FLOAT_EQ(channel_1[i], 2.0f); + } +} + +TEST(SimdUtilsTest, DeinterleaveUnalignedFloatTest) { + float interleaved[kFullSize]; + AudioBuffer planar(kNumStereoChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kFullSize; ++i) { + interleaved[i] = (i % 2 == 0) ? 1.0f : 2.0f; + } + + // Clear the output buffers. + planar.Clear(); + + // Test the case where input is unaligned. + DeinterleaveStereo(kHalfSize, interleaved, channel_0.begin(), + channel_1.begin()); + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_FLOAT_EQ(channel_0[i], 1.0f); + EXPECT_FLOAT_EQ(channel_1[i], 2.0f); + } +} + +TEST(SimdUtilsTest, InterleaveQuadInt16Test) { + AudioBuffer::AlignedInt16Vector interleaved(kQuadSize); + AudioBuffer::AlignedInt16Vector workspace(kPentSize); + AudioBuffer::AlignedInt16Vector channel_0(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_1(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_2(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_3(kHalfSize); + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = kOne; + channel_1[i] = kTwo; + channel_2[i] = kThree; + channel_3[i] = kFour; + } + + InterleaveQuad(kHalfSize, channel_0.data(), channel_1.data(), + channel_2.data(), channel_3.data(), workspace.data(), + interleaved.data()); + + for (size_t i = 0; i < kQuadSize; ++i) { + const int16_t value = static_cast<int16_t>(1 + (i % kNumQuadChannels)); + EXPECT_FLOAT_EQ(interleaved[i], value); + } +} + +TEST(SimdUtilsTest, DeinterleaveQuadInt16Test) { + AudioBuffer::AlignedInt16Vector interleaved(kQuadSize); + AudioBuffer::AlignedInt16Vector workspace(kPentSize); + AudioBuffer::AlignedInt16Vector channel_0(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_1(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_2(kHalfSize); + AudioBuffer::AlignedInt16Vector channel_3(kHalfSize); + + // Fill the aligned input buffer. + for (size_t i = 0; i < kQuadSize; ++i) { + interleaved[i] = static_cast<int16_t>(1 + (i % kNumQuadChannels)); + } + + // Clear the output buffers. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = static_cast<int16_t>(0); + channel_1[i] = static_cast<int16_t>(0); + channel_2[i] = static_cast<int16_t>(0); + channel_3[i] = static_cast<int16_t>(0); + } + + // Test the case where input is aligned. + DeinterleaveQuad(kHalfSize, interleaved.data(), workspace.data(), + channel_0.data(), channel_1.data(), channel_2.data(), + channel_3.data()); + + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_EQ(channel_0[i], kOne); + EXPECT_EQ(channel_1[i], kTwo); + EXPECT_EQ(channel_2[i], kThree); + EXPECT_EQ(channel_3[i], kFour); + } +} + +TEST(SimdUtilsTest, InterleaveQuadFloatTest) { + AudioBuffer interleaved(kNumMonoChannels, kQuadSize); + AudioBuffer workspace(kNumMonoChannels, kPentSize); + AudioBuffer planar(kNumQuadChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + AudioBuffer::Channel& channel_2 = planar[2]; + AudioBuffer::Channel& channel_3 = planar[3]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kHalfSize; ++i) { + channel_0[i] = 1.0f; + channel_1[i] = 2.0f; + channel_2[i] = 3.0f; + channel_3[i] = 4.0f; + } + + InterleaveQuad(kHalfSize, channel_0.begin(), channel_1.begin(), + channel_2.begin(), channel_3.begin(), workspace[0].begin(), + interleaved[0].begin()); + + for (size_t i = 0; i < kQuadSize; ++i) { + const float value = static_cast<float>(1 + (i % kNumQuadChannels)); + EXPECT_FLOAT_EQ(interleaved[0][i], value); + } +} + +TEST(SimdUtilsTest, DeinterleaveQuadFloatTest) { + AudioBuffer interleaved(kNumMonoChannels, kQuadSize); + AudioBuffer workspace(kNumMonoChannels, kPentSize); + AudioBuffer planar(kNumQuadChannels, kHalfSize); + AudioBuffer::Channel& channel_0 = planar[0]; + AudioBuffer::Channel& channel_1 = planar[1]; + AudioBuffer::Channel& channel_2 = planar[2]; + AudioBuffer::Channel& channel_3 = planar[3]; + + // Fill the aligned input buffer. + for (size_t i = 0; i < kQuadSize; ++i) { + interleaved[0][i] = static_cast<float>(1 + (i % kNumQuadChannels)); + } + + // Clear the output buffers. + planar.Clear(); + + // Test the case where input is aligned. + DeinterleaveQuad(kHalfSize, interleaved[0].begin(), workspace[0].begin(), + channel_0.begin(), channel_1.begin(), channel_2.begin(), + channel_3.begin()); + + for (size_t i = 0; i < kHalfSize; ++i) { + EXPECT_FLOAT_EQ(channel_0[i], 1.0f); + EXPECT_FLOAT_EQ(channel_1[i], 2.0f); + EXPECT_FLOAT_EQ(channel_2[i], 3.0f); + EXPECT_FLOAT_EQ(channel_3[i], 4.0f); + } +} + +TEST(SimdUtilsTest, Int16FromFloatTest) { + AudioBuffer float_buffer(kNumMonoChannels, kInputSize); + float_buffer.Clear(); + + AudioBuffer::AlignedInt16Vector int_buffer(kInputSize); + + for (size_t i = 0; i < kInputSize; ++i) { + float_buffer[0][i] = kFloatInput[i]; + } + + Int16FromFloat(kInputSize, &(float_buffer[0][0]), int_buffer.data()); + + for (size_t i = 0; i < kInputSize; ++i) { + EXPECT_NEAR(int_buffer[i], kIntInput[i], kIntEpsilon); + } +} + +TEST(SimdUtilsTest, FloatFromInt16Test) { + AudioBuffer float_buffer(kNumMonoChannels, kInputSize); + float_buffer.Clear(); + + AudioBuffer::AlignedInt16Vector int_buffer(kInputSize); + + for (size_t i = 0; i < kInputSize; ++i) { + int_buffer[i] = static_cast<int16_t>(kIntInput[i]); + } + + FloatFromInt16(kInputSize, int_buffer.data(), &(float_buffer[0][0])); + + for (size_t i = 0; i < kInputSize; i += 2) { + EXPECT_NEAR(float_buffer[0][i], kFloatInput[i], kFloatEpsilon); + } +} + +} // namespace + +} // namespace vraudio |