You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
4.3 KiB
C++
157 lines
4.3 KiB
C++
#include "decoder.hpp"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
using namespace std::string_literals;
|
|
|
|
static constexpr std::size_t INPUTS = 2;
|
|
|
|
static void FindCh(
|
|
std::string_view sv, const std::vector<Channel::E>& channels,
|
|
std::vector<int>& reorder, unsigned cur)
|
|
{
|
|
for (std::size_t i = 0; i < channels.size(); ++i)
|
|
if (auto x = Channel::NAMES_PIPEWIRE[channels[i]];
|
|
strlen(x) == sv.size() && !strncasecmp(x, sv.data(), sv.size()))
|
|
if (reorder[i] == -1)
|
|
{
|
|
reorder[i] = cur;
|
|
return;
|
|
}
|
|
else
|
|
throw std::runtime_error{"Duplicate channel " + std::string{sv}};
|
|
throw std::runtime_error{"Invalid channel " + std::string{sv}};
|
|
}
|
|
|
|
int Main(int argc, char** argv)
|
|
{
|
|
float rate = 1/48000.f;
|
|
std::string_view channel_map;
|
|
Decoder dec;
|
|
{
|
|
Option opts[] = {
|
|
{ "help", "", "Show help" },
|
|
{
|
|
"sample_rate", "FLOAT", "Sets sample rate of input (default: 48000 Hz)",
|
|
[&](std::string_view sv)
|
|
{ rate = 1 / FromString<float>(sv); },
|
|
},
|
|
{
|
|
"channel_map", "CHANNEL_MAP",
|
|
"Set comma separated list of output channel order (help to get list of available channels)",
|
|
[&](std::string_view sv) { channel_map = sv; },
|
|
},
|
|
};
|
|
|
|
opts[0].apply = [&](std::string_view)
|
|
{
|
|
std::cerr << "Usage " << argv[0] << " [--options] < input > output\n";
|
|
PrintHelp(opts);
|
|
dec.PrintHelp();
|
|
throw Exit{0};
|
|
};
|
|
|
|
auto dopt = dec.GetOptions();
|
|
ParseArgs(argc, argv, [&](std::string_view k, std::string_view v)
|
|
{
|
|
if (HandleOption(opts, k, v)) return;
|
|
auto dopt = dec.GetOptions();
|
|
if (HandleOption(dopt, k, v)) return;
|
|
throw ParseError("Unknown option "s + std::string{k});
|
|
});
|
|
}
|
|
|
|
std::vector<int> channel_reorder;
|
|
if (channel_map == "help")
|
|
{
|
|
for (const auto m : dec.GetChannels())
|
|
std::cerr << Channel::NAMES_PIPEWIRE[m] << '\n';
|
|
return 0;
|
|
}
|
|
else if (!channel_map.empty())
|
|
{
|
|
auto chs = dec.GetChannels();
|
|
channel_reorder.resize(chs.size(), -1);
|
|
std::size_t begin = 0;
|
|
unsigned i = 0;
|
|
while (true)
|
|
{
|
|
auto end = channel_map.find_first_of(',', begin);
|
|
auto it = channel_map.substr(begin, end-begin);
|
|
FindCh(it, chs, channel_reorder, i++);
|
|
if (end == std::string_view::npos) break;
|
|
begin = end + 1;
|
|
}
|
|
|
|
if (i != chs.size())
|
|
throw std::runtime_error{"Not enough channels in reorder"};
|
|
}
|
|
|
|
auto block_size = dec.GetBlockSize();
|
|
auto read_size = INPUTS * (block_size ? block_size : 8192);
|
|
auto outputs = dec.GetChannels().size();
|
|
auto skip = outputs * dec.GetDelay();
|
|
std::vector<float> in_buf(read_size);
|
|
|
|
auto do_skip = [&](std::span<const float>& data)
|
|
{
|
|
auto cskip = std::min<std::size_t>(skip, data.size());
|
|
data = data.subspan(cskip);
|
|
skip -= cskip;
|
|
};
|
|
|
|
std::uint64_t total_read = 0, total_write = 0;
|
|
std::vector<float> reorder_buf;
|
|
auto wr = [&](std::span<const float> data)
|
|
{
|
|
if (const auto n = channel_reorder.size())
|
|
{
|
|
reorder_buf.resize(data.size());
|
|
for (std::size_t i = 0; i < data.size(); i += n)
|
|
{
|
|
std::array<float, Channel::N_CHANNELS> buf;
|
|
std::memcpy(buf.data(), data.data() + i, sizeof(float) * n);
|
|
for (std::size_t j = 0; j < n; ++j)
|
|
reorder_buf[i + std::size_t(channel_reorder[j])] = buf[j];
|
|
}
|
|
data = reorder_buf;
|
|
}
|
|
|
|
if (fwrite(data.data(), sizeof(float), data.size(), stdout) != data.size())
|
|
throw std::runtime_error("Write error");
|
|
total_write += data.size();
|
|
};
|
|
|
|
std::size_t n_read;
|
|
while (true)
|
|
{
|
|
n_read = fread(in_buf.data(), sizeof(float), read_size, stdin);
|
|
total_read += n_read;
|
|
if (n_read != read_size) break;
|
|
|
|
auto res = dec.Decode(rate, std::span{in_buf});
|
|
do_skip(res);
|
|
wr(res);
|
|
}
|
|
|
|
// partial read, finish up
|
|
auto os_read = (total_read / INPUTS) * outputs;
|
|
auto do_chunk = [&]()
|
|
{
|
|
auto res = dec.Decode(rate, std::span{in_buf});
|
|
do_skip(res);
|
|
wr(res.subspan(0, std::min(os_read - total_write, res.size())));
|
|
};
|
|
|
|
std::memset(in_buf.data() + n_read, 0, sizeof(float) * (read_size - n_read));
|
|
do_chunk();
|
|
if (os_read == total_write) return 0;
|
|
|
|
std::memset(in_buf.data(), 0, sizeof(float) * in_buf.size());
|
|
while (os_read > total_write) do_chunk();
|
|
|
|
return 0;
|
|
}
|