blob: 0baf37b1d84fd3b9db79f730f9987f32f55b9481 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <gtest/gtest.h>
#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <sys/param.h>
#include <memory>
extern "C" {
#include "cras_fmt_conv_ops.h"
#include "cras_types.h"
}
static uint8_t* AllocateRandomBytes(size_t size) {
uint8_t* buf = (uint8_t*)malloc(size);
while (size--)
buf[size] = rand() & 0xff;
return buf;
}
using U8Ptr = std::unique_ptr<uint8_t[], decltype(free)*>;
using S16LEPtr = std::unique_ptr<int16_t[], decltype(free)*>;
using S243LEPtr = std::unique_ptr<uint8_t[], decltype(free)*>;
using S24LEPtr = std::unique_ptr<int32_t[], decltype(free)*>;
using S32LEPtr = std::unique_ptr<int32_t[], decltype(free)*>;
using FloatPtr = std::unique_ptr<float[], decltype(free)*>;
static U8Ptr CreateU8(size_t size) {
uint8_t* buf = AllocateRandomBytes(size * sizeof(uint8_t));
U8Ptr ret(buf, free);
return ret;
}
static S16LEPtr CreateS16LE(size_t size) {
uint8_t* buf = AllocateRandomBytes(size * sizeof(int16_t));
S16LEPtr ret(reinterpret_cast<int16_t*>(buf), free);
return ret;
}
static S243LEPtr CreateS243LE(size_t size) {
uint8_t* buf = AllocateRandomBytes(size * sizeof(uint8_t) * 3);
S243LEPtr ret(buf, free);
return ret;
}
static S24LEPtr CreateS24LE(size_t size) {
uint8_t* buf = AllocateRandomBytes(size * sizeof(int32_t));
S24LEPtr ret(reinterpret_cast<int32_t*>(buf), free);
return ret;
}
static S32LEPtr CreateS32LE(size_t size) {
uint8_t* buf = AllocateRandomBytes(size * sizeof(int32_t));
S32LEPtr ret(reinterpret_cast<int32_t*>(buf), free);
return ret;
}
static FloatPtr CreateFloat(size_t size) {
float* buf = (float*)malloc(size * sizeof(float));
while (size--)
buf[size] = (float)(rand() & 0xff) / 0xfff;
FloatPtr ret(buf, free);
return ret;
}
static int32_t ToS243LE(const uint8_t* in) {
int32_t ret = 0;
ret |= in[2];
ret <<= 8;
ret |= in[1];
ret <<= 8;
ret |= in[0];
return ret;
}
static int16_t S16AddAndClip(int16_t a, int16_t b) {
int32_t sum;
sum = (int32_t)a + (int32_t)b;
sum = MAX(sum, SHRT_MIN);
sum = MIN(sum, SHRT_MAX);
return sum;
}
// Test U8 to S16_LE conversion.
TEST(FormatConverterOpsTest, ConvertU8ToS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
U8Ptr src = CreateU8(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
convert_u8_to_s16le(src.get(), frames * in_ch, (uint8_t*)dst.get());
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int16_t)((uint16_t)((int16_t)(int8_t)src[i] - 0x80) << 8),
dst[i]);
}
}
// Test S24_3LE to S16_LE conversion.
TEST(FormatConverterOpsTest, ConvertS243LEToS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S243LEPtr src = CreateS243LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
convert_s243le_to_s16le(src.get(), frames * in_ch, (uint8_t*)dst.get());
uint8_t* p = src.get();
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int16_t)(ToS243LE(p) >> 8), dst[i]);
p += 3;
}
}
// Test S24_LE to S16_LE conversion.
TEST(FormatConverterOpsTest, ConvertS24LEToS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S24LEPtr src = CreateS24LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
convert_s24le_to_s16le((uint8_t*)src.get(), frames * in_ch,
(uint8_t*)dst.get());
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int16_t)(src[i] >> 8), dst[i]);
}
}
// Test S32_LE to S16_LE conversion.
TEST(FormatConverterOpsTest, ConvertS32LEToS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S32LEPtr src = CreateS32LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
convert_s32le_to_s16le((uint8_t*)src.get(), frames * in_ch,
(uint8_t*)dst.get());
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int16_t)(src[i] >> 16), dst[i]);
}
}
// Test S16_LE to U8 conversion.
TEST(FormatConverterOpsTest, ConvertS16LEToU8) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S16LEPtr src = CreateS16LE(frames * in_ch);
U8Ptr dst = CreateU8(frames * out_ch);
convert_s16le_to_u8((uint8_t*)src.get(), frames * in_ch, dst.get());
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((uint8_t)(int8_t)((src[i] >> 8) + 0x80), dst[i]);
}
}
// Test S16_LE to S24_3LE conversion.
TEST(FormatConverterOpsTest, ConvertS16LEToS243LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S16LEPtr src = CreateS16LE(frames * in_ch);
S243LEPtr dst = CreateS243LE(frames * out_ch);
convert_s16le_to_s243le((uint8_t*)src.get(), frames * in_ch, dst.get());
uint8_t* p = dst.get();
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int32_t)((uint32_t)src[i] << 8) & 0x00ffffff,
ToS243LE(p) & 0x00ffffff);
p += 3;
}
}
// Test S16_LE to S24_LE conversion.
TEST(FormatConverterOpsTest, ConvertS16LEToS24LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S16LEPtr src = CreateS16LE(frames * in_ch);
S24LEPtr dst = CreateS24LE(frames * out_ch);
convert_s16le_to_s24le((uint8_t*)src.get(), frames * in_ch,
(uint8_t*)dst.get());
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int32_t)((uint32_t)src[i] << 8) & 0x00ffffff,
dst[i] & 0x00ffffff);
}
}
// Test S16_LE to S32_LE conversion.
TEST(FormatConverterOpsTest, ConvertS16LEToS32LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 2;
S16LEPtr src = CreateS16LE(frames * in_ch);
S32LEPtr dst = CreateS32LE(frames * out_ch);
convert_s16le_to_s32le((uint8_t*)src.get(), frames * in_ch,
(uint8_t*)dst.get());
for (size_t i = 0; i < frames * in_ch; ++i) {
EXPECT_EQ((int32_t)((uint32_t)src[i] << 16) & 0xffffff00,
dst[i] & 0xffffff00);
}
}
// Test Mono to Stereo conversion. S16_LE.
TEST(FormatConverterOpsTest, MonoToStereoS16LE) {
const size_t frames = 4096;
const size_t in_ch = 1;
const size_t out_ch = 2;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret =
s16_mono_to_stereo((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(src[i], dst[i * 2 + 0]);
EXPECT_EQ(src[i], dst[i * 2 + 1]);
}
}
// Test Stereo to Mono conversion. S16_LE.
TEST(FormatConverterOpsTest, StereoToMonoS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
for (size_t i = 0; i < frames; ++i) {
src[i * 2 + 0] = 13450;
src[i * 2 + 1] = -13449;
}
size_t ret =
s16_stereo_to_mono((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(1, dst[i]);
}
}
// Test Stereo to Mono conversion. S16_LE, Overflow.
TEST(FormatConverterOpsTest, StereoToMonoS16LEOverflow) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
for (size_t i = 0; i < frames; ++i) {
src[i * 2 + 0] = 0x7fff;
src[i * 2 + 1] = 1;
}
size_t ret =
s16_stereo_to_mono((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(0x7fff, dst[i]);
}
}
// Test Stereo to Mono conversion. S16_LE, Underflow.
TEST(FormatConverterOpsTest, StereoToMonoS16LEUnderflow) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
for (size_t i = 0; i < frames; ++i) {
src[i * 2 + 0] = -0x8000;
src[i * 2 + 1] = -0x1;
}
size_t ret =
s16_stereo_to_mono((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(-0x8000, dst[i]);
}
}
// Test Mono to 5.1 conversion. S16_LE, Center.
TEST(FormatConverterOpsTest, MonoTo51S16LECenter) {
const size_t frames = 4096;
const size_t in_ch = 1;
const size_t out_ch = 6;
const size_t left = 0;
const size_t right = 1;
const size_t center = 4;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_mono_to_51(left, right, center, (uint8_t*)src.get(), frames,
(uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < 6; ++k) {
if (k == center)
EXPECT_EQ(src[i], dst[i * 6 + k]);
else
EXPECT_EQ(0, dst[i * 6 + k]);
}
}
}
// Test Mono to 5.1 conversion. S16_LE, LeftRight.
TEST(FormatConverterOpsTest, MonoTo51S16LELeftRight) {
const size_t frames = 4096;
const size_t in_ch = 1;
const size_t out_ch = 6;
const size_t left = 0;
const size_t right = 1;
const size_t center = -1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_mono_to_51(left, right, center, (uint8_t*)src.get(), frames,
(uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < 6; ++k) {
if (k == left)
EXPECT_EQ(src[i] / 2, dst[i * 6 + k]);
else if (k == right)
EXPECT_EQ(src[i] / 2, dst[i * 6 + k]);
else
EXPECT_EQ(0, dst[i * 6 + k]);
}
}
}
// Test Mono to 5.1 conversion. S16_LE, Unknown.
TEST(FormatConverterOpsTest, MonoTo51S16LEUnknown) {
const size_t frames = 4096;
const size_t in_ch = 1;
const size_t out_ch = 6;
const size_t left = -1;
const size_t right = -1;
const size_t center = -1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_mono_to_51(left, right, center, (uint8_t*)src.get(), frames,
(uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < 6; ++k) {
if (k == 0)
EXPECT_EQ(src[i], dst[i * 6 + k]);
else
EXPECT_EQ(0, dst[6 * i + k]);
}
}
}
// Test Stereo to 5.1 conversion. S16_LE, Center.
TEST(FormatConverterOpsTest, StereoTo51S16LECenter) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 6;
const size_t left = -1;
const size_t right = 1;
const size_t center = 4;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_stereo_to_51(left, right, center, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < 6; ++k) {
if (k == center)
EXPECT_EQ(S16AddAndClip(src[i * 2], src[i * 2 + 1]), dst[i * 6 + k]);
else
EXPECT_EQ(0, dst[i * 6 + k]);
}
}
}
// Test Quad to 5.1 conversion. S16_LE.
TEST(FormatConverterOpsTest, QuadTo51S16LE) {
const size_t frames = 4096;
const size_t in_ch = 4;
const size_t out_ch = 6;
const unsigned int fl_quad = 0;
const unsigned int fr_quad = 1;
const unsigned int rl_quad = 2;
const unsigned int rr_quad = 3;
const unsigned int fl_51 = 0;
const unsigned int fr_51 = 1;
const unsigned int center_51 = 2;
const unsigned int lfe_51 = 3;
const unsigned int rl_51 = 4;
const unsigned int rr_51 = 5;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_quad_to_51(fl_51, fr_51, rl_51, rr_51, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(0, dst[i * 6 + center_51]);
EXPECT_EQ(0, dst[i * 6 + lfe_51]);
EXPECT_EQ(src[i * 4 + fl_quad], dst[i * 6 + fl_51]);
EXPECT_EQ(src[i * 4 + fr_quad], dst[i * 6 + fr_51]);
EXPECT_EQ(src[i * 4 + rl_quad], dst[i * 6 + rl_51]);
EXPECT_EQ(src[i * 4 + rr_quad], dst[i * 6 + rr_51]);
}
}
// Test Stereo to 5.1 conversion. S16_LE, LeftRight.
TEST(FormatConverterOpsTest, StereoTo51S16LELeftRight) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 6;
const size_t left = 0;
const size_t right = 1;
const size_t center = -1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_stereo_to_51(left, right, center, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < 6; ++k) {
if (k == left)
EXPECT_EQ(src[i * 2 + 0], dst[i * 6 + k]);
else if (k == right)
EXPECT_EQ(src[i * 2 + 1], dst[i * 6 + k]);
else
EXPECT_EQ(0, dst[i * 6 + k]);
}
}
}
// Test Stereo to 5.1 conversion. S16_LE, Unknown.
TEST(FormatConverterOpsTest, StereoTo51S16LEUnknown) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 6;
const size_t left = -1;
const size_t right = -1;
const size_t center = -1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_stereo_to_51(left, right, center, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < 6; ++k) {
if (k == 0 || k == 1)
EXPECT_EQ(src[i * 2 + k], dst[i * 6 + k]);
else
EXPECT_EQ(0, dst[i * 6 + k]);
}
}
}
// Test 5.1 to Stereo conversion. S16_LE.
TEST(FormatConverterOpsTest, _51ToStereoS16LE) {
const size_t frames = 4096;
const size_t in_ch = 6;
const size_t out_ch = 2;
const size_t left = 0;
const size_t right = 1;
const size_t center = 2;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret =
s16_51_to_stereo((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
/* Use the normalized_factor from the left channel = 1 / (|1| + |0.707|)
* to prevent mixing overflow.
*/
const float normalized_factor = 0.585;
for (size_t i = 0; i < frames; ++i) {
int16_t half_center = src[i * 6 + center] * 0.707 * normalized_factor;
int16_t l = normalized_factor * src[i * 6 + left] + half_center;
int16_t r = normalized_factor * src[i * 6 + right] + half_center;
EXPECT_EQ(l, dst[i * 2 + left]);
EXPECT_EQ(r, dst[i * 2 + right]);
}
}
// Test 5.1 to Quad conversion. S16_LE.
TEST(FormatConverterOpsTest, _51ToQuadS16LE) {
const size_t frames = 4096;
const size_t in_ch = 6;
const size_t out_ch = 4;
const unsigned int fl_quad = 0;
const unsigned int fr_quad = 1;
const unsigned int rl_quad = 2;
const unsigned int rr_quad = 3;
const unsigned int fl_51 = 0;
const unsigned int fr_51 = 1;
const unsigned int center_51 = 2;
const unsigned int lfe_51 = 3;
const unsigned int rl_51 = 4;
const unsigned int rr_51 = 5;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_51_to_quad((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
/* Use normalized_factor from the left channel = 1 / (|1| + |0.707| + |0.5|)
* to prevent overflow. */
const float normalized_factor = 0.453;
for (size_t i = 0; i < frames; ++i) {
int16_t half_center = src[i * 6 + center_51] * 0.707 * normalized_factor;
int16_t lfe = src[6 * i + lfe_51] * 0.5 * normalized_factor;
int16_t fl = normalized_factor * src[6 * i + fl_51] + half_center + lfe;
int16_t fr = normalized_factor * src[6 * i + fr_51] + half_center + lfe;
int16_t rl = normalized_factor * src[6 * i + rl_51] + lfe;
int16_t rr = normalized_factor * src[6 * i + rr_51] + lfe;
EXPECT_EQ(fl, dst[4 * i + fl_quad]);
EXPECT_EQ(fr, dst[4 * i + fr_quad]);
EXPECT_EQ(rl, dst[4 * i + rl_quad]);
EXPECT_EQ(rr, dst[4 * i + rr_quad]);
}
}
// Test Stereo to Quad conversion. S16_LE, Specify.
TEST(FormatConverterOpsTest, StereoToQuadS16LESpecify) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 4;
const size_t front_left = 2;
const size_t front_right = 3;
const size_t rear_left = 0;
const size_t rear_right = 1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret =
s16_stereo_to_quad(front_left, front_right, rear_left, rear_right,
(uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(src[i * 2 + 0], dst[i * 4 + front_left]);
EXPECT_EQ(src[i * 2 + 0], dst[i * 4 + rear_left]);
EXPECT_EQ(src[i * 2 + 1], dst[i * 4 + front_right]);
EXPECT_EQ(src[i * 2 + 1], dst[i * 4 + rear_right]);
}
}
// Test Stereo to Quad conversion. S16_LE, Default.
TEST(FormatConverterOpsTest, StereoToQuadS16LEDefault) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 4;
const size_t front_left = -1;
const size_t front_right = -1;
const size_t rear_left = -1;
const size_t rear_right = -1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret =
s16_stereo_to_quad(front_left, front_right, rear_left, rear_right,
(uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
EXPECT_EQ(src[i * 2 + 0], dst[i * 4 + 0]);
EXPECT_EQ(src[i * 2 + 0], dst[i * 4 + 2]);
EXPECT_EQ(src[i * 2 + 1], dst[i * 4 + 1]);
EXPECT_EQ(src[i * 2 + 1], dst[i * 4 + 3]);
}
}
// Test Quad to Stereo conversion. S16_LE, Specify.
TEST(FormatConverterOpsTest, QuadToStereoS16LESpecify) {
const size_t frames = 4096;
const size_t in_ch = 4;
const size_t out_ch = 2;
const size_t front_left = 2;
const size_t front_right = 3;
const size_t rear_left = 0;
const size_t rear_right = 1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret =
s16_quad_to_stereo(front_left, front_right, rear_left, rear_right,
(uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
int16_t left =
S16AddAndClip(src[i * 4 + front_left], src[i * 4 + rear_left] / 4);
int16_t right =
S16AddAndClip(src[i * 4 + front_right], src[i * 4 + rear_right] / 4);
EXPECT_EQ(left, dst[i * 2 + 0]);
EXPECT_EQ(right, dst[i * 2 + 1]);
}
}
// Test Quad to Stereo conversion. S16_LE, Default.
TEST(FormatConverterOpsTest, QuadToStereoS16LEDefault) {
const size_t frames = 4096;
const size_t in_ch = 4;
const size_t out_ch = 2;
const size_t front_left = -1;
const size_t front_right = -1;
const size_t rear_left = -1;
const size_t rear_right = -1;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret =
s16_quad_to_stereo(front_left, front_right, rear_left, rear_right,
(uint8_t*)src.get(), frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
int16_t left = S16AddAndClip(src[i * 4 + 0], src[i * 4 + 2] / 4);
int16_t right = S16AddAndClip(src[i * 4 + 1], src[i * 4 + 3] / 4);
EXPECT_EQ(left, dst[i * 2 + 0]);
EXPECT_EQ(right, dst[i * 2 + 1]);
}
}
// Test Stereo to 3ch conversion. S16_LE.
TEST(FormatConverterOpsTest, StereoTo3chS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 3;
struct cras_audio_format fmt = {
.format = SND_PCM_FORMAT_S16_LE,
.frame_rate = 48000,
.num_channels = 3,
};
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_default_all_to_all(&fmt, in_ch, out_ch, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
int32_t sum = 0;
for (size_t k = 0; k < in_ch; ++k)
sum += (int32_t)src[i * in_ch + k];
src[i * in_ch + 0] = (int16_t)(sum / (int32_t)in_ch);
}
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < out_ch; ++k)
EXPECT_EQ(src[i * in_ch + 0], dst[i * out_ch + k]);
}
}
// Test 6ch to 8ch conversion. S16_LE.
TEST(FormatConverterOpsTest, 6chTo8chS16LE) {
const size_t frames = 65536;
const size_t in_ch = 6;
const size_t out_ch = 8;
struct cras_audio_format fmt = {
.format = SND_PCM_FORMAT_S16_LE,
.frame_rate = 48000,
.num_channels = 8,
};
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < in_ch; k++) {
src[i * in_ch + k] = (k == 0) ? (INT16_MIN + (int16_t)i) : 0;
}
}
size_t ret = s16_default_all_to_all(&fmt, in_ch, out_ch, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
src[i * in_ch + 0] /= (int16_t)in_ch;
for (size_t k = 0; k < out_ch; ++k)
EXPECT_EQ(src[i * in_ch + 0], dst[i * out_ch + k]);
}
}
// Test Multiply with Coef. S16_LE.
TEST(FormatConverterOpsTest, MultiplyWithCoefS16LE) {
const size_t buf_size = 4096;
S16LEPtr buf = CreateS16LE(buf_size);
FloatPtr coef = CreateFloat(buf_size);
int16_t ret = s16_multiply_buf_with_coef(coef.get(), buf.get(), buf_size);
int32_t exp = 0;
for (size_t i = 0; i < buf_size; ++i)
exp += coef[i] * buf[i];
exp = MIN(MAX(exp, SHRT_MIN), SHRT_MAX);
EXPECT_EQ((int16_t)exp, ret);
}
// Test Convert Channels. S16_LE.
TEST(FormatConverterOpsTest, ConvertChannelsS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 3;
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
FloatPtr ch_conv_mtx = CreateFloat(out_ch * in_ch);
std::unique_ptr<float*[]> mtx(new float*[out_ch]);
for (size_t i = 0; i < out_ch; ++i)
mtx[i] = &ch_conv_mtx[i * in_ch];
size_t ret =
s16_convert_channels(mtx.get(), in_ch, out_ch, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t fr = 0; fr < frames; ++fr) {
for (size_t i = 0; i < out_ch; ++i) {
int16_t exp = 0;
for (size_t k = 0; k < in_ch; ++k)
exp += mtx[i][k] * src[fr * in_ch + k];
exp = MIN(MAX(exp, SHRT_MIN), SHRT_MAX);
EXPECT_EQ(exp, dst[fr * out_ch + i]);
}
}
}
// Test Stereo to 20ch conversion. S16_LE.
TEST(FormatConverterOpsTest, TwoToTwentyS16LE) {
const size_t frames = 4096;
const size_t in_ch = 2;
const size_t out_ch = 20;
struct cras_audio_format fmt = {
.format = SND_PCM_FORMAT_S16_LE,
.frame_rate = 48000,
.num_channels = 20,
};
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_some_to_some(&fmt, in_ch, out_ch, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
size_t k;
// Input channles should be directly copied over.
for (k = 0; k < in_ch; ++k) {
EXPECT_EQ(src[i * in_ch + k], dst[i * out_ch + k]);
}
// The rest should be zeroed.
for (; k < out_ch; ++k) {
EXPECT_EQ(0, dst[i * out_ch + k]);
}
}
}
// Test 20ch to Stereo. S16_LE.
TEST(FormatConverterOpsTest, TwentyToTwoS16LE) {
const size_t frames = 4096;
const size_t in_ch = 20;
const size_t out_ch = 2;
struct cras_audio_format fmt = {
.format = SND_PCM_FORMAT_S16_LE,
.frame_rate = 48000,
.num_channels = 2,
};
S16LEPtr src = CreateS16LE(frames * in_ch);
S16LEPtr dst = CreateS16LE(frames * out_ch);
size_t ret = s16_some_to_some(&fmt, in_ch, out_ch, (uint8_t*)src.get(),
frames, (uint8_t*)dst.get());
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
size_t k;
// Input channles should be directly copied over.
for (k = 0; k < out_ch; ++k) {
EXPECT_EQ(src[i * in_ch + k], dst[i * out_ch + k]);
}
}
}
extern "C" {} // extern "C"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}