/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see .
*/
#include
#include
#include
#include
#include
#include
using namespace dwarfs;
namespace {
template
std::vector make_sine(int bits, size_t length, double period) {
std::vector rv(length);
double amplitude = (1 << bits) / 2;
for (size_t i = 0; i < length; ++i) {
rv[i] = static_cast(
amplitude * std::sin(2 * std::numbers::pi * i / period) - 0.5);
}
return rv;
}
template
std::vector multiplex(std::vector> const& in) {
auto samples = in.front().size();
auto channels = in.size();
std::vector out(channels * samples);
for (size_t i = 0; i < samples; ++i) {
for (size_t c = 0; c < channels; ++c) {
out[i * channels + c] = in.at(c).at(i);
}
}
return out;
}
template
std::vector
make_test_data(int channels, int samples, int bytes, int bits,
pcm_sample_endianness end, pcm_sample_signedness sig,
pcm_sample_padding pad) {
std::vector> data;
for (int c = 0; c < channels; ++c) {
data.emplace_back(
make_sine(bits, samples, 3.1 * ((599 * (c + 1)) % 256)));
}
auto muxed = multiplex(data);
std::vector out(bytes * channels * samples);
pcm_sample_transformer xfm(end, sig, pad, bytes, bits);
xfm.pack(out, muxed);
return out;
}
struct data_params {
data_params(int channels, int samples, int bytes, int bits)
: num_channels{channels}
, num_samples{samples}
, bytes_per_sample{bytes}
, bits_per_sample{bits} {}
int num_channels;
int num_samples;
int bytes_per_sample;
int bits_per_sample;
};
std::ostream& operator<<(std::ostream& os, data_params const& p) {
os << "{channels=" << p.num_channels << ", samples=" << p.num_samples
<< ", bytes=" << p.bytes_per_sample << ", bits=" << p.bits_per_sample
<< "}";
return os;
}
std::vector const data_parameters{
// clang-format off
{ 1, 1000, 2, 16},
{ 3, 1000, 1, 8},
{ 1, 1000, 2, 12},
{ 1, 100000, 3, 20},
{ 8, 10000, 3, 20},
{ 4, 10000, 4, 20},
{ 4, 10000, 4, 24},
{ 4, 10000, 3, 24},
{ 7, 799999, 4, 32},
// clang-format on
};
} // namespace
TEST(flac_compressor, sine) {
{
auto test = make_sine(8, 5, 4.0);
std::vector ref{0, 127, 0, -128, 0};
EXPECT_EQ(test, ref);
}
{
auto test = make_sine(5, 5, 4.0);
std::vector ref{0, 15, 0, -16, 0};
EXPECT_EQ(test, ref);
}
{
auto test = make_sine(16, 5, 4.0);
std::vector ref{0, 32767, 0, -32768, 0};
EXPECT_EQ(test, ref);
}
{
auto test = make_sine(12, 5, 4.0);
std::vector ref{0, 2047, 0, -2048, 0};
EXPECT_EQ(test, ref);
}
}
TEST(flac_compressor, basic) {
nlohmann::json meta{{"endianness", "little"}, {"signedness", "signed"},
{"padding", "msb"}, {"bytes_per_sample", 2},
{"bits_per_sample", 16}, {"number_of_channels", 2}};
auto const data =
make_test_data(2, 1000, 2, 16, pcm_sample_endianness::Little,
pcm_sample_signedness::Signed, pcm_sample_padding::Msb);
block_compressor comp("flac");
auto compressed = comp.compress(data, meta.dump());
EXPECT_LT(compressed.size(), data.size() / 2);
auto decompressed = block_decompressor::decompress(
compression_type::FLAC, compressed.data(), compressed.size());
EXPECT_EQ(data, decompressed);
}
class flac_param : public testing::TestWithParam<
std::tuple> {};
TEST_P(flac_param, combinations) {
auto [end, sig, pad, param] = GetParam();
nlohmann::json meta{
{"endianness", end == pcm_sample_endianness::Big ? "big" : "little"},
{"signedness",
sig == pcm_sample_signedness::Signed ? "signed" : "unsigned"},
{"padding", pad == pcm_sample_padding::Msb ? "msb" : "lsb"},
{"bytes_per_sample", param.bytes_per_sample},
{"bits_per_sample", param.bits_per_sample},
{"number_of_channels", param.num_channels},
};
auto const data = make_test_data(param.num_channels, param.num_samples,
param.bytes_per_sample,
param.bits_per_sample, end, sig, pad);
block_compressor comp("flac");
auto compressed = comp.compress(data, meta.dump());
EXPECT_LT(compressed.size(), data.size() / 2);
auto decompressed = block_decompressor::decompress(
compression_type::FLAC, compressed.data(), compressed.size());
EXPECT_EQ(data, decompressed);
}
INSTANTIATE_TEST_SUITE_P(
dwarfs, flac_param,
::testing::Combine(::testing::Values(pcm_sample_endianness::Big,
pcm_sample_endianness::Little),
::testing::Values(pcm_sample_signedness::Signed,
pcm_sample_signedness::Unsigned),
::testing::Values(pcm_sample_padding::Lsb,
pcm_sample_padding::Msb),
::testing::ValuesIn(data_parameters)));