aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2022-09-12 01:11:30 -0700
committerChris Robinson <[email protected]>2022-09-12 01:12:07 -0700
commitc027ef7bf7acb590f20ce8edd51c8db254f3b651 (patch)
tree6f6edd2460a9247733c0419cdf9f571d8ea4ec79
parent583bd0e47c0d3c579c9e5b716e112cc4ab5c6960 (diff)
Simplify ambdec loading
-rw-r--r--core/ambdec.cpp392
1 files changed, 156 insertions, 236 deletions
diff --git a/core/ambdec.cpp b/core/ambdec.cpp
index 31eaf391..fcaf0388 100644
--- a/core/ambdec.cpp
+++ b/core/ambdec.cpp
@@ -5,11 +5,14 @@
#include <algorithm>
#include <cctype>
+#include <cstdarg>
#include <cstddef>
+#include <cstdio>
#include <iterator>
#include <sstream>
#include <string>
+#include "albit.h"
#include "alfstream.h"
#include "alspan.h"
#include "core/logging.h"
@@ -17,36 +20,6 @@
namespace {
-int readline(std::istream &f, std::string &output)
-{
- while(f.good() && f.peek() == '\n')
- f.ignore();
-
- return std::getline(f, output) && !output.empty();
-}
-
-bool read_clipped_line(std::istream &f, std::string &buffer)
-{
- while(readline(f, buffer))
- {
- std::size_t pos{0};
- while(pos < buffer.length() && std::isspace(buffer[pos]))
- pos++;
- buffer.erase(0, pos);
-
- std::size_t cmtpos{buffer.find_first_of('#')};
- if(cmtpos < buffer.length())
- buffer.resize(cmtpos);
- while(!buffer.empty() && std::isspace(buffer.back()))
- buffer.pop_back();
-
- if(!buffer.empty())
- return true;
- }
- return false;
-}
-
-
std::string read_word(std::istream &f)
{
std::string ret;
@@ -58,221 +31,208 @@ bool is_at_end(const std::string &buffer, std::size_t endpos)
{
while(endpos < buffer.length() && std::isspace(buffer[endpos]))
++endpos;
- return !(endpos < buffer.length());
+ return !(endpos < buffer.length() && buffer[endpos] != '#');
}
-al::optional<std::string> load_ambdec_speakers(const al::span<AmbDecConf::SpeakerConf> spkrs,
- std::istream &f, std::string &buffer)
+enum class ReaderScope {
+ Global,
+ Speakers,
+ LFMatrix,
+ HFMatrix,
+};
+
+#ifdef __USE_MINGW_ANSI_STDIO
+[[gnu::format(gnu_printf,2,3)]]
+#else
+[[gnu::format(printf,2,3)]]
+#endif
+al::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
{
- size_t cur_speaker{0};
- while(cur_speaker < spkrs.size())
+ al::optional<std::string> ret;
+ auto &str = ret.emplace();
+
+ str.resize(256);
+ int printed{std::snprintf(&str[0], str.length(), "Line %zu: ", linenum)};
+ if(printed < 0) printed = 0;
+ auto plen = std::min(static_cast<size_t>(printed), str.length());
+
+ std::va_list args, args2;
+ va_start(args, fmt);
+ va_copy(args2, args);
+ const int msglen{std::vsnprintf(&str[plen], str.size()-plen, fmt, args)};
+ if(unlikely(msglen >= 0 && static_cast<size_t>(msglen) >= str.size()-plen))
{
- std::istringstream istr{buffer};
+ str.resize(static_cast<size_t>(msglen) + plen + 1u);
+ std::vsnprintf(&str[plen], str.size()-plen, fmt, args);
+ }
+ va_end(args2);
+ va_end(args);
- std::string cmd{read_word(istr)};
- if(cmd.empty())
- {
- if(!read_clipped_line(f, buffer))
- return al::make_optional<std::string>("Unexpected end of file");
- continue;
- }
+ return ret;
+}
- if(cmd == "add_spkr")
- {
- AmbDecConf::SpeakerConf &spkr = spkrs[cur_speaker++];
- const size_t spkr_num{cur_speaker};
-
- istr >> spkr.Name;
- if(istr.fail()) WARN("Name not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Distance;
- if(istr.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Azimuth;
- if(istr.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Elevation;
- if(istr.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Connection;
- if(istr.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num);
- }
- else
- return al::make_optional("Unexpected speakers command: "+cmd);
+} // namespace
- istr.clear();
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
- buffer.clear();
- }
+AmbDecConf::~AmbDecConf() = default;
- return al::nullopt;
-}
-al::optional<std::string> load_ambdec_matrix(const al::span<float,MaxAmbiOrder+1> gains,
- AmbDecConf::CoeffArray *matrix, const std::size_t maxrow, std::istream &f, std::string &buffer)
+al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
- bool gotgains{false};
- std::size_t cur{0u};
- while(cur < maxrow)
+ al::ifstream f{fname};
+ if(!f.is_open())
+ return al::make_optional(std::string("Failed to open file \"")+fname+"\"");
+
+ ReaderScope scope{ReaderScope::Global};
+ size_t speaker_pos{0};
+ size_t lfmatrix_pos{0};
+ size_t hfmatrix_pos{0};
+ size_t linenum{0};
+
+ std::string buffer;
+ while(f.good() && std::getline(f, buffer))
{
+ ++linenum;
+
std::istringstream istr{buffer};
+ std::string command{read_word(istr)};
+ if(command.empty() || command[0] == '#')
+ continue;
- std::string cmd{read_word(istr)};
- if(cmd.empty())
+ if(command == "/}")
{
- if(!read_clipped_line(f, buffer))
- return al::make_optional<std::string>("Unexpected end of file");
+ if(scope == ReaderScope::Global)
+ return make_error(linenum, "Unexpected /} in global scope");
+ scope = ReaderScope::Global;
continue;
}
- if(cmd == "order_gain")
+ if(scope == ReaderScope::Speakers)
{
- std::size_t curgain{0u};
- float value;
- while(istr.good())
+ if(command == "add_spkr")
{
- istr >> value;
- if(istr.fail()) break;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk on gain "+std::to_string(curgain+1)+": "+
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
- if(curgain < gains.size())
- gains[curgain++] = value;
+ if(speaker_pos == NumSpeakers)
+ return make_error(linenum, "Too many speakers specified");
+
+ AmbDecConf::SpeakerConf &spkr = Speakers[speaker_pos++];
+ istr >> spkr.Name;
+ istr >> spkr.Distance;
+ istr >> spkr.Azimuth;
+ istr >> spkr.Elevation;
+ istr >> spkr.Connection;
}
- std::fill(gains.begin()+curgain, gains.end(), 0.0f);
- gotgains = true;
+ else
+ return make_error(linenum, "Unexpected speakers command: %s", command.c_str());
}
- else if(cmd == "add_row")
+ else if(scope == ReaderScope::LFMatrix || scope == ReaderScope::HFMatrix)
{
- AmbDecConf::CoeffArray &mtxrow = matrix[cur++];
- std::size_t curidx{0u};
- float value{};
- while(istr.good())
+ auto &gains = (scope == ReaderScope::LFMatrix) ? LFOrderGain : HFOrderGain;
+ auto *matrix = (scope == ReaderScope::LFMatrix) ? LFMatrix : HFMatrix;
+ auto &pos = (scope == ReaderScope::LFMatrix) ? lfmatrix_pos : hfmatrix_pos;
+
+ if(command == "order_gain")
{
- istr >> value;
- if(istr.fail()) break;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk on matrix element "+
- std::to_string(curidx)+"x"+std::to_string(cur-1)+": "+
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
- if(curidx < mtxrow.size())
- mtxrow[curidx++] = value;
+ size_t toread{(ChanMask > Ambi3OrderMask) ? 5u : 4u};
+ std::size_t curgain{0u};
+ float value{};
+ while(toread)
+ {
+ --toread;
+ istr >> value;
+ if(curgain < al::size(gains))
+ gains[curgain++] = value;
+ }
}
- std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f);
- }
- else
- return al::make_optional("Unexpected matrix command: "+cmd);
-
- istr.clear();
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
- buffer.clear();
- }
-
- if(!gotgains)
- return al::make_optional<std::string>("Matrix order_gain not specified");
- return al::nullopt;
-}
-
-} // namespace
-
-AmbDecConf::~AmbDecConf() = default;
-
+ else if(command == "add_row")
+ {
+ if(pos == NumSpeakers)
+ return make_error(linenum, "Too many matrix rows specified");
-al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
-{
- al::ifstream f{fname};
- if(!f.is_open())
- return al::make_optional<std::string>("Failed to open file");
+ unsigned int mask{ChanMask};
- bool speakers_loaded{false};
- bool matrix_loaded{false};
- bool lfmatrix_loaded{false};
- std::string buffer;
- while(read_clipped_line(f, buffer))
- {
- std::istringstream istr{buffer};
+ AmbDecConf::CoeffArray &mtxrow = matrix[pos++];
+ mtxrow.fill(0.0f);
- std::string command{read_word(istr)};
- if(command.empty())
- return al::make_optional("Malformed line: "+buffer);
+ std::size_t curidx{0u};
+ float value{};
+ while(mask)
+ {
+ auto idx = al::countr_zero(mask);
+ mask &= ~(1u << idx);
- if(command == "/description")
- readline(istr, Description);
+ istr >> value;
+ if(curidx < mtxrow.size())
+ mtxrow[curidx++] = value;
+ }
+ }
+ else
+ return make_error(linenum, "Unexpected matrix command: %s", command.c_str());
+ }
+ // Global scope commands
+ else if(command == "/description")
+ {
+ while(istr.good() && std::isspace(istr.peek()))
+ istr.ignore();
+ std::getline(istr, Description);
+ }
else if(command == "/version")
{
+ if(Version)
+ return make_error(linenum, "Duplicate version definition");
istr >> Version;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk after version: " +
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
if(Version != 3)
- return al::make_optional("Unsupported version: "+std::to_string(Version));
+ return make_error(linenum, "Unsupported version: %d", Version);
}
else if(command == "/dec/chan_mask")
{
if(ChanMask)
- return al::make_optional<std::string>("Duplicate chan_mask definition");
-
+ return make_error(linenum, "Duplicate chan_mask definition");
istr >> std::hex >> ChanMask >> std::dec;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk after mask: " +
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
- if(!ChanMask)
- return al::make_optional("Invalid chan_mask: "+std::to_string(ChanMask));
+ if(!ChanMask || ChanMask > Ambi4OrderMask)
+ return make_error(linenum, "Invalid chan_mask: 0x%x", ChanMask);
+ if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa)
+ return make_error(linenum, "FuMa not compatible with over third-order");
}
else if(command == "/dec/freq_bands")
{
if(FreqBands)
- return al::make_optional<std::string>("Duplicate freq_bands");
-
+ return make_error(linenum, "Duplicate freq_bands");
istr >> FreqBands;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk after freq_bands: " +
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
-
if(FreqBands != 1 && FreqBands != 2)
- return al::make_optional("Invalid freq_bands: "+std::to_string(FreqBands));
+ return make_error(linenum, "Invalid freq_bands: %u", FreqBands);
}
else if(command == "/dec/speakers")
{
if(NumSpeakers)
- return al::make_optional<std::string>("Duplicate speakers");
-
+ return make_error(linenum, "Duplicate speakers");
istr >> NumSpeakers;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk after speakers: " +
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
-
if(!NumSpeakers)
- return al::make_optional("Invalid speakers: "+std::to_string(NumSpeakers));
+ return make_error(linenum, "Invalid speakers: %zu", NumSpeakers);
Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers);
}
else if(command == "/dec/coeff_scale")
{
if(CoeffScale != AmbDecScale::Unset)
- return al::make_optional<std::string>("Duplicate coeff_scale");
+ return make_error(linenum, "Duplicate coeff_scale");
- std::string scale = read_word(istr);
+ std::string scale{read_word(istr)};
if(scale == "n3d") CoeffScale = AmbDecScale::N3D;
else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
else
- return al::make_optional("Unexpected coeff_scale: "+scale);
+ return make_error(linenum, "Unexpected coeff_scale: %s", scale.c_str());
+
+ if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa)
+ return make_error(linenum, "FuMa not compatible with over third-order");
}
else if(command == "/opt/xover_freq")
{
istr >> XOverFreq;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk after xover_freq: " +
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
}
else if(command == "/opt/xover_ratio")
{
istr >> XOverRatio;
- if(!istr.eof() && !std::isspace(istr.peek()))
- return al::make_optional("Extra junk after xover_ratio: " +
- buffer.substr(static_cast<std::size_t>(istr.tellg())));
}
else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp"
|| command == "/opt/delay_comp" || command == "/opt/level_comp")
@@ -283,33 +243,15 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
else if(command == "/speakers/{")
{
if(!NumSpeakers)
- return al::make_optional<std::string>("Speakers defined without a count");
-
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
- buffer.clear();
-
- if(auto err = load_ambdec_speakers({Speakers.get(), NumSpeakers}, f, buffer))
- return err;
- speakers_loaded = true;
-
- if(!read_clipped_line(f, buffer))
- return al::make_optional<std::string>("Unexpected end of file");
- std::istringstream istr2{buffer};
- std::string endmark{read_word(istr2)};
- if(endmark != "/}")
- return al::make_optional("Expected /} after speaker definitions, got "+endmark);
- istr.swap(istr2);
+ return make_error(linenum, "Speakers defined without a count");
+ scope = ReaderScope::Speakers;
}
else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
{
if(!NumSpeakers)
- return al::make_optional<std::string>("Matrix defined without a count");
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
- buffer.clear();
+ return make_error(linenum, "Matrix defined without a speaker count");
+ if(!ChanMask)
+ return make_error(linenum, "Matrix defined without a channel mask");
if(!Matrix)
{
@@ -321,65 +263,43 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
if(FreqBands == 1)
{
if(command != "/matrix/{")
- return al::make_optional(
- "Unexpected \""+command+"\" type for a single-band decoder");
- if(auto err = load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
- return err;
- matrix_loaded = true;
+ return make_error(linenum, "Unexpected \"%s\" for a single-band decoder",
+ command.c_str());
+ scope = ReaderScope::HFMatrix;
}
else
{
if(command == "/lfmatrix/{")
- {
- if(auto err=load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer))
- return err;
- lfmatrix_loaded = true;
- }
+ scope = ReaderScope::LFMatrix;
else if(command == "/hfmatrix/{")
- {
- if(auto err=load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
- return err;
- matrix_loaded = true;
- }
+ scope = ReaderScope::HFMatrix;
else
- return al::make_optional(
- "Unexpected \""+command+"\" type for a dual-band decoder");
+ return make_error(linenum, "Unexpected \"%s\" for a dual-band decoder",
+ command.c_str());
}
-
- if(!read_clipped_line(f, buffer))
- return al::make_optional<std::string>("Unexpected end of file");
- std::istringstream istr2{buffer};
- std::string endmark{read_word(istr2)};
- if(endmark != "/}")
- return al::make_optional("Expected /} after matrix definitions, got "+endmark);
- istr.swap(istr2);
}
else if(command == "/end")
{
const auto endpos = static_cast<std::size_t>(istr.tellg());
if(!is_at_end(buffer, endpos))
- return al::make_optional("Extra junk on end: " + buffer.substr(endpos));
+ return make_error(linenum, "Extra junk on end: %s", buffer.substr(endpos).c_str());
- if(ChanMask == 0)
- return al::make_optional<std::string>("No channel mask defined");
- if(!speakers_loaded || !matrix_loaded || (FreqBands == 2 && !lfmatrix_loaded))
- return al::make_optional<std::string>("No decoder defined");
+ if(speaker_pos < NumSpeakers || hfmatrix_pos < NumSpeakers
+ || (FreqBands == 2 && lfmatrix_pos < NumSpeakers))
+ return make_error(linenum, "Incomplete decoder definition");
if(CoeffScale == AmbDecScale::Unset)
- return al::make_optional<std::string>("No coefficient scaling defined");
-
- if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa)
- return al::make_optional<std::string>("FuMa not compatible with over third-order");
+ return make_error(linenum, "No coefficient scaling defined");
return al::nullopt;
}
else
- return al::make_optional("Unexpected command: " + command);
+ return make_error(linenum, "Unexpected command: %s", command.c_str());
istr.clear();
const auto endpos = static_cast<std::size_t>(istr.tellg());
if(!is_at_end(buffer, endpos))
- return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
+ return make_error(linenum, "Extra junk on line: %s", buffer.substr(endpos).c_str());
buffer.clear();
}
- return al::make_optional<std::string>("Unexpected end of file");
+ return make_error(linenum, "Unexpected end of file");
}