Bladeren bron

audio: improve test suite and comments for sample conversion

wip/image-kernel
Sam Hocevar 9 maanden geleden
bovenliggende
commit
1c61616ec8
2 gewijzigde bestanden met toevoegingen van 99 en 35 verwijderingen
  1. +10
    -8
      include/lol/private/audio/stream.h
  2. +89
    -27
      t/audio.cpp

+ 10
- 8
include/lol/private/audio/stream.h Bestand weergeven

@@ -64,21 +64,23 @@ public:
}
else
{
// Conversion between integer types:
// - convert to unsigned
// - shift right or multiply by a magic constant such as 0x0101 to propagate bytes
// - convert back to signed if necessary
// Most operations are done using the UBIG type, which is an unsigned integer type
// at least as large as FROM and TO.
// When converting between integer types, we first convert to an unsigned
// type of same size as source (e.g. int16_t → uint16_t) to ensure that all
// operations will happen modulo n (not guaranteed with signed types).
// The next step is to shift right (drop bits) or promote left (multiplying
// by a magic constant such as 0x1010101 or 0x10001). This happens using the
// UBIG type, which is an unsigned integer type at least as large as FROM
// and TO.
// Finally, we convert back to signed (e.g. uint16_t → int16_t) if necessary.
using UFROM = std::make_unsigned_t<FROM>;
using UTO = std::make_unsigned_t<TO>;
using UBIG = std::conditional_t<(sizeof(FROM) > sizeof(TO)), UFROM, UTO>;

UBIG constexpr div = UBIG(1) << 8 * (sizeof(UBIG) - sizeof(UTO));
UBIG constexpr mul = std::numeric_limits<UBIG>::max() / std::numeric_limits<UFROM>::max();
UBIG constexpr div = UBIG(1) << 8 * (sizeof(UBIG) - sizeof(UTO));
auto tmp = UFROM(UFROM(x) - UFROM(std::numeric_limits<FROM>::min())) * mul / div;

return TO(UTO(tmp) + UTO(std::numeric_limits<TO>::min()));
return TO(tmp + UTO(std::numeric_limits<TO>::min()));
}
}
};


+ 89
- 27
t/audio.cpp Bestand weergeven

@@ -5,22 +5,30 @@ TEST_CASE("sample conversion: float|double ←→ float|double")
{
auto cv1 = lol::audio::sample::convert<float, float>;
CHECK(cv1(-1.0f) == -1.0f);
CHECK(cv1(-0.5f) == -0.5f);
CHECK(cv1( 0.0f) == 0.0f);
CHECK(cv1( 0.5f) == 0.5f);
CHECK(cv1( 1.0f) == 1.0f);

auto cv2 = lol::audio::sample::convert<float, double>;
CHECK(cv2(-1.0f) == -1.0);
CHECK(cv2(-0.5f) == -0.5);
CHECK(cv2( 0.0f) == 0.0);
CHECK(cv2( 0.5f) == 0.5);
CHECK(cv2( 1.0f) == 1.0);

auto cv3 = lol::audio::sample::convert<double, float>;
CHECK(cv3(-1.0) == -1.0f);
CHECK(cv3(-0.5) == -0.5f);
CHECK(cv3( 0.0) == 0.0f);
CHECK(cv3( 0.5) == 0.5f);
CHECK(cv3( 1.0) == 1.0f);

auto cv4 = lol::audio::sample::convert<double, double>;
CHECK(cv4(-1.0) == -1.0);
CHECK(cv4(-0.5) == -0.5);
CHECK(cv4( 0.0) == 0.0);
CHECK(cv4( 0.5) == 0.5);
CHECK(cv4( 1.0) == 1.0);
}

@@ -94,7 +102,6 @@ TEST_CASE("sample conversion: int16|uint16 → float")
auto cv1 = lol::audio::sample::convert<int16_t, float>;
CHECK(cv1(-0x8000) == -1.0f);
CHECK(cv1( 0x7fff) == 1.0f);

for (int n = -0x8000; n < 0x7fff; ++n)
{
CAPTURE(n);
@@ -104,7 +111,6 @@ TEST_CASE("sample conversion: int16|uint16 → float")
auto cv2 = lol::audio::sample::convert<uint16_t, float>;
CHECK(cv2(0x0000) == -1.0f);
CHECK(cv2(0xffff) == 1.0f);

for (int n = 0x0000; n < 0xffff; ++n)
{
CAPTURE(n);
@@ -115,23 +121,38 @@ TEST_CASE("sample conversion: int16|uint16 → float")
TEST_CASE("sample conversion: int8 ←→ uint8")
{
auto cv1 = lol::audio::sample::convert<int8_t, uint8_t>;
CHECK(cv1(-0x80) == 0x00);
CHECK(cv1(-0x40) == 0x40);
CHECK(cv1(-0x01) == 0x7f);
CHECK(cv1( 0x00) == 0x80);
CHECK(cv1( 0x3f) == 0xbf);
CHECK(cv1( 0x7f) == 0xff);
for (int n = -0x80; n <= 0x7f; ++n)
{
CAPTURE(n);
CHECK(cv1(n) == n + 0x80);
}

auto cv2 = lol::audio::sample::convert<uint8_t, int8_t>;
CHECK(cv2(0x00) == -0x80);
CHECK(cv2(0x40) == -0x40);
CHECK(cv2(0x7f) == -0x01);
CHECK(cv2(0x80) == 0x00);
CHECK(cv2(0xbf) == 0x3f);
CHECK(cv2(0xff) == 0x7f);
for (int n = 0x00; n <= 0xff; ++n)
{
CAPTURE(n);
CHECK(cv2(n) == n - 0x80);
}
}

TEST_CASE("sample conversion: int16 ←→ uint16")
{
auto cv1 = lol::audio::sample::convert<int16_t, uint16_t>;
for (int n = -0x8000; n <= 0x7fff; ++n)
{
CAPTURE(n);
CHECK(cv1(n) == n + 0x8000);
}

auto cv2 = lol::audio::sample::convert<uint16_t, int16_t>;
for (int n = 0x0000; n <= 0xffff; ++n)
{
CAPTURE(n);
CHECK(cv2(n) == n - 0x8000);
}
}

TEST_CASE("sample conversion: int16|uint16 → int8")
TEST_CASE("sample conversion: int16|uint16 → int8|uint8")
{
auto cv1 = lol::audio::sample::convert<int16_t, int8_t>;
CHECK(cv1(-0x8000) == -0x80);
@@ -152,22 +173,63 @@ TEST_CASE("sample conversion: int16|uint16 → int8")
CHECK(cv2(0x80ff) == 0x00);
CHECK(cv2(0xbfff) == 0x3f);
CHECK(cv2(0xffff) == 0x7f);

auto cv3 = lol::audio::sample::convert<int16_t, uint8_t>;
CHECK(cv3(-0x8000) == 0x00);
CHECK(cv3(-0x4000) == 0x40);
CHECK(cv3(-0x0080) == 0x7f);
CHECK(cv3(-0x0001) == 0x7f);
CHECK(cv3( 0x0000) == 0x80);
CHECK(cv3( 0x00ff) == 0x80);
CHECK(cv3( 0x3fff) == 0xbf);
CHECK(cv3( 0x7fff) == 0xff);

auto cv4 = lol::audio::sample::convert<uint16_t, uint8_t>;
CHECK(cv4(0x0000) == 0x00);
CHECK(cv4(0x4000) == 0x40);
CHECK(cv4(0x7f80) == 0x7f);
CHECK(cv4(0x7fff) == 0x7f);
CHECK(cv4(0x8000) == 0x80);
CHECK(cv4(0x80ff) == 0x80);
CHECK(cv4(0xbfff) == 0xbf);
CHECK(cv4(0xffff) == 0xff);
}

TEST_CASE("sample conversion: uint8 → int16|uint16")
{
auto cv1 = lol::audio::sample::convert<uint8_t, int16_t>;
for (int n = 0x00; n <= 0xff; ++n)
{
CAPTURE(n);
CHECK(cv1(n) == n * 0x101 - 0x8000);
}

auto cv2 = lol::audio::sample::convert<uint8_t, uint16_t>;
for (int n = 0x00; n <= 0xff; ++n)
{
CAPTURE(n);
CHECK(cv2(n) == n * 0x101);
}
}

TEST_CASE("sample conversion: int8 → float → int8")
{
auto cv1 = lol::audio::sample::convert<int8_t, float>;
auto cv2 = lol::audio::sample::convert<float, int8_t>;
CHECK(cv2(cv1(-0x80)) == -0x80);
CHECK(cv2(cv1(-0x7f)) == -0x7f);
CHECK(cv2(cv1(-0x40)) == -0x40);
CHECK(cv2(cv1(-0x20)) == -0x20);
CHECK(cv2(cv1(-0x02)) == -0x02);
CHECK(cv2(cv1(-0x01)) == -0x01);
CHECK(cv2(cv1( 0x00)) == 0x00);
CHECK(cv2(cv1( 0x01)) == 0x01);
CHECK(cv2(cv1( 0x02)) == 0x02);
CHECK(cv2(cv1( 0x20)) == 0x20);
CHECK(cv2(cv1( 0x40)) == 0x40);
CHECK(cv2(cv1( 0x7f)) == 0x7f);
for (int n = -0x80; n <= 0x7f; ++n)
{
CAPTURE(n);
CHECK(cv2(cv1(n)) == n);
}
}

TEST_CASE("sample conversion: uint8 → float → uint8")
{
auto cv1 = lol::audio::sample::convert<uint8_t, float>;
auto cv2 = lol::audio::sample::convert<float, uint8_t>;
for (int n = 0x00; n <= 0xff; ++n)
{
CAPTURE(n);
CHECK(cv2(cv1(n)) == n);
}
}

Laden…
Annuleren
Opslaan