Add tests for the audio bitdepth conversions
And fix some bugs in it, and make it not rely on undefined behavior.
This commit is contained in:
parent
585e9489d9
commit
b9c75d8706
2 changed files with 47 additions and 19 deletions
|
@ -41,7 +41,7 @@ public:
|
||||||
std::vector<uint8_t> src_buf(count * src_bytes_per_sample * channels);
|
std::vector<uint8_t> src_buf(count * src_bytes_per_sample * channels);
|
||||||
source->GetAudio(src_buf.data(), start, count);
|
source->GetAudio(src_buf.data(), start, count);
|
||||||
|
|
||||||
int16_t *dest = reinterpret_cast<int16_t*>(buf);
|
auto dest = static_cast<int16_t*>(buf);
|
||||||
|
|
||||||
for (int64_t i = 0; i < count * channels; ++i) {
|
for (int64_t i = 0; i < count * channels; ++i) {
|
||||||
int64_t sample = 0;
|
int64_t sample = 0;
|
||||||
|
@ -49,18 +49,18 @@ public:
|
||||||
// 8 bits per sample is assumed to be unsigned with a bias of 127,
|
// 8 bits per sample is assumed to be unsigned with a bias of 127,
|
||||||
// while everything else is assumed to be signed with zero bias
|
// while everything else is assumed to be signed with zero bias
|
||||||
if (src_bytes_per_sample == 1)
|
if (src_bytes_per_sample == 1)
|
||||||
sample = src_buf[i] - 127;
|
sample = src_buf[i] - 128;
|
||||||
else {
|
else {
|
||||||
for (int j = 0; j < src_bytes_per_sample; ++j) {
|
for (int j = src_bytes_per_sample; j > 0; --j) {
|
||||||
sample <<= 8;
|
sample <<= 8;
|
||||||
sample += src_buf[i * src_bytes_per_sample + j];
|
sample += src_buf[i * src_bytes_per_sample + j - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<size_t>(src_bytes_per_sample) > sizeof(Target))
|
if (static_cast<size_t>(src_bytes_per_sample) > sizeof(Target))
|
||||||
sample >>= (src_bytes_per_sample - sizeof(Target)) * 8;
|
sample /= 1 << (src_bytes_per_sample - sizeof(Target)) * 8;
|
||||||
else if (static_cast<size_t>(src_bytes_per_sample) < sizeof(Target))
|
else if (static_cast<size_t>(src_bytes_per_sample) < sizeof(Target))
|
||||||
sample <<= (sizeof(Target) - src_bytes_per_sample ) * 8;
|
sample *= 1 << (sizeof(Target) - src_bytes_per_sample ) * 8;
|
||||||
|
|
||||||
dest[i] = static_cast<Target>(sample);
|
dest[i] = static_cast<Target>(sample);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,25 +54,28 @@ TEST(lagi_audio, dummy_rejects_non_dummy_url) {
|
||||||
ASSERT_EQ(nullptr, provider.get());
|
ASSERT_EQ(nullptr, provider.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Sample=uint16_t>
|
||||||
struct TestAudioProvider : agi::AudioProvider {
|
struct TestAudioProvider : agi::AudioProvider {
|
||||||
TestAudioProvider(int64_t duration = 90) {
|
int bias = 0;
|
||||||
|
|
||||||
|
TestAudioProvider(int64_t duration = 90, int rate=48000) {
|
||||||
channels = 1;
|
channels = 1;
|
||||||
num_samples = duration * 48000;
|
num_samples = duration * 48000;
|
||||||
decoded_samples = num_samples;
|
decoded_samples = num_samples;
|
||||||
sample_rate = 48000;
|
sample_rate = rate;
|
||||||
bytes_per_sample = 2;
|
bytes_per_sample = sizeof(Sample);
|
||||||
float_samples = false;
|
float_samples = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
|
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
|
||||||
auto out = static_cast<uint16_t *>(buf);
|
auto out = static_cast<Sample *>(buf);
|
||||||
for (int64_t end = start + count; start < end; ++start)
|
for (int64_t end = start + count; start < end; ++start)
|
||||||
*out++ = (uint16_t)start;
|
*out++ = (Sample)(start + bias);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(lagi_audio, before_sample_zero) {
|
TEST(lagi_audio, before_sample_zero) {
|
||||||
TestAudioProvider provider;
|
TestAudioProvider<> provider;
|
||||||
|
|
||||||
uint16_t buff[16];
|
uint16_t buff[16];
|
||||||
memset(buff, sizeof(buff), 1);
|
memset(buff, sizeof(buff), 1);
|
||||||
|
@ -85,7 +88,7 @@ TEST(lagi_audio, before_sample_zero) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, after_end) {
|
TEST(lagi_audio, after_end) {
|
||||||
TestAudioProvider provider(1);
|
TestAudioProvider<> provider(1);
|
||||||
|
|
||||||
uint16_t buff[16];
|
uint16_t buff[16];
|
||||||
memset(buff, sizeof(buff), 1);
|
memset(buff, sizeof(buff), 1);
|
||||||
|
@ -115,7 +118,7 @@ TEST(lagi_audio, save_audio_clip) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, get_with_volume) {
|
TEST(lagi_audio, get_with_volume) {
|
||||||
TestAudioProvider provider;
|
TestAudioProvider<> provider;
|
||||||
uint16_t buff[4];
|
uint16_t buff[4];
|
||||||
|
|
||||||
provider.GetAudioWithVolume(buff, 0, 4, 1.0);
|
provider.GetAudioWithVolume(buff, 0, 4, 1.0);
|
||||||
|
@ -138,14 +141,14 @@ TEST(lagi_audio, get_with_volume) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, volume_should_clamp_rather_than_wrap) {
|
TEST(lagi_audio, volume_should_clamp_rather_than_wrap) {
|
||||||
TestAudioProvider provider;
|
TestAudioProvider<> provider;
|
||||||
uint16_t buff[1];
|
uint16_t buff[1];
|
||||||
provider.GetAudioWithVolume(buff, 30000, 1, 2.0);
|
provider.GetAudioWithVolume(buff, 30000, 1, 2.0);
|
||||||
EXPECT_EQ(SHRT_MAX, buff[0]);
|
EXPECT_EQ(SHRT_MAX, buff[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, ram_cache) {
|
TEST(lagi_audio, ram_cache) {
|
||||||
auto provider = agi::CreateRAMAudioProvider(agi::make_unique<TestAudioProvider>());
|
auto provider = agi::CreateRAMAudioProvider(agi::make_unique<TestAudioProvider<>>());
|
||||||
EXPECT_EQ(1, provider->GetChannels());
|
EXPECT_EQ(1, provider->GetChannels());
|
||||||
EXPECT_EQ(90 * 48000, provider->GetNumSamples());
|
EXPECT_EQ(90 * 48000, provider->GetNumSamples());
|
||||||
EXPECT_EQ(48000, provider->GetSampleRate());
|
EXPECT_EQ(48000, provider->GetSampleRate());
|
||||||
|
@ -162,7 +165,7 @@ TEST(lagi_audio, ram_cache) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, hd_cache) {
|
TEST(lagi_audio, hd_cache) {
|
||||||
auto provider = agi::CreateHDAudioProvider(agi::make_unique<TestAudioProvider>(), agi::Path().Decode("?temp"));
|
auto provider = agi::CreateHDAudioProvider(agi::make_unique<TestAudioProvider<>>(), agi::Path().Decode("?temp"));
|
||||||
while (provider->GetDecodedSamples() != provider->GetNumSamples()) agi::util::sleep_for(0);
|
while (provider->GetDecodedSamples() != provider->GetNumSamples()) agi::util::sleep_for(0);
|
||||||
|
|
||||||
uint16_t buff[512];
|
uint16_t buff[512];
|
||||||
|
@ -172,10 +175,35 @@ TEST(lagi_audio, hd_cache) {
|
||||||
ASSERT_EQ(static_cast<uint16_t>((1 << 22) - 256 + i), buff[i]);
|
ASSERT_EQ(static_cast<uint16_t>((1 << 22) - 256 + i), buff[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(lagi_audio, convert_8bit) {
|
||||||
|
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<TestAudioProvider<uint8_t>>());
|
||||||
|
|
||||||
|
int16_t data[256];
|
||||||
|
provider->GetAudio(data, 0, 256);
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
ASSERT_EQ((i - 128) * 256, data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(lagi_audio, convert_32bit) {
|
||||||
|
auto src = agi::make_unique<TestAudioProvider<uint32_t>>(100000);
|
||||||
|
src->bias = INT_MIN;
|
||||||
|
auto provider = agi::CreateConvertAudioProvider(std::move(src));
|
||||||
|
|
||||||
|
int16_t sample;
|
||||||
|
provider->GetAudio(&sample, 0, 1);
|
||||||
|
EXPECT_EQ(SHRT_MIN, sample);
|
||||||
|
|
||||||
|
provider->GetAudio(&sample, 1LL << 31, 1);
|
||||||
|
EXPECT_EQ(0, sample);
|
||||||
|
|
||||||
|
provider->GetAudio(&sample, (1LL << 32) - 1, 1);
|
||||||
|
EXPECT_EQ(SHRT_MAX, sample);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, pcm_simple) {
|
TEST(lagi_audio, pcm_simple) {
|
||||||
auto path = agi::Path().Decode("?temp/pcm_simple");
|
auto path = agi::Path().Decode("?temp/pcm_simple");
|
||||||
{
|
{
|
||||||
TestAudioProvider provider;
|
TestAudioProvider<> provider;
|
||||||
agi::SaveAudioClip(&provider, path, 0, 1000);
|
agi::SaveAudioClip(&provider, path, 0, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +227,7 @@ TEST(lagi_audio, pcm_simple) {
|
||||||
TEST(lagi_audio, pcm_truncated) {
|
TEST(lagi_audio, pcm_truncated) {
|
||||||
auto path = agi::Path().Decode("?temp/pcm_truncated");
|
auto path = agi::Path().Decode("?temp/pcm_truncated");
|
||||||
{
|
{
|
||||||
TestAudioProvider provider;
|
TestAudioProvider<> provider;
|
||||||
agi::SaveAudioClip(&provider, path, 0, 1000);
|
agi::SaveAudioClip(&provider, path, 0, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue