openal-soft/core/ambdec.cpp

379 lines
13 KiB
C++
Raw Normal View History

2016-03-14 09:04:03 -07:00
#include "config.h"
#include "ambdec.h"
#include <algorithm>
2019-07-29 19:59:48 -07:00
#include <cctype>
#include <cstddef>
#include <iterator>
2018-11-04 15:24:24 -08:00
#include <sstream>
2019-07-29 19:59:48 -07:00
#include <string>
2018-11-03 19:51:23 -07:00
2019-09-22 12:23:41 -07:00
#include "alfstream.h"
2020-12-17 01:25:33 -08:00
#include "core/logging.h"
2016-03-14 09:04:03 -07:00
2018-11-04 15:24:24 -08:00
namespace {
2016-03-14 09:04:03 -07:00
template<typename T, std::size_t N>
constexpr inline std::size_t size(const T(&)[N]) noexcept
{ return N; }
2018-11-04 15:24:24 -08:00
int readline(std::istream &f, std::string &output)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
while(f.good() && f.peek() == '\n')
f.ignore();
2016-03-14 09:04:03 -07:00
return std::getline(f, output) && !output.empty();
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
bool read_clipped_line(std::istream &f, std::string &buffer)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
while(readline(f, buffer))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
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;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
return false;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
std::string read_word(std::istream &f)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
std::string ret;
f >> ret;
return ret;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
bool is_at_end(const std::string &buffer, std::size_t endpos)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
while(endpos < buffer.length() && std::isspace(buffer[endpos]))
++endpos;
2018-12-12 21:18:31 -08:00
return !(endpos < buffer.length());
2016-03-14 09:04:03 -07:00
}
2020-12-25 09:26:24 -08:00
al::optional<std::string> load_ambdec_speakers(AmbDecConf::SpeakerConf *spkrs,
const std::size_t num_speakers, std::istream &f, std::string &buffer)
2016-03-14 09:04:03 -07:00
{
size_t cur_speaker{0};
while(cur_speaker < num_speakers)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
std::istringstream istr{buffer};
std::string cmd{read_word(istr)};
2018-11-04 15:24:24 -08:00
if(cmd.empty())
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
if(!read_clipped_line(f, buffer))
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Unexpected end of file");
continue;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(cmd == "add_spkr")
2016-03-14 09:04:03 -07:00
{
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);
2016-03-14 09:04:03 -07:00
}
else
2020-12-25 09:26:24 -08:00
return al::make_optional("Unexpected speakers command: "+cmd);
2016-03-14 09:04:03 -07:00
2018-11-04 15:24:24 -08:00
istr.clear();
2018-12-12 21:18:31 -08:00
const auto endpos = static_cast<std::size_t>(istr.tellg());
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
2018-11-04 15:24:24 -08:00
buffer.clear();
2016-03-14 09:04:03 -07:00
}
2020-12-25 09:26:24 -08:00
return al::nullopt;
2016-03-14 09:04:03 -07:00
}
2020-12-25 09:26:24 -08:00
al::optional<std::string> load_ambdec_matrix(float (&gains)[MaxAmbiOrder+1],
AmbDecConf::CoeffArray *matrix, const std::size_t maxrow, std::istream &f, std::string &buffer)
2016-03-14 09:04:03 -07:00
{
bool gotgains{false};
std::size_t cur{0u};
2016-03-14 09:04:03 -07:00
while(cur < maxrow)
{
2018-11-04 15:24:24 -08:00
std::istringstream istr{buffer};
std::string cmd{read_word(istr)};
2018-11-04 15:24:24 -08:00
if(cmd.empty())
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
if(!read_clipped_line(f, buffer))
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Unexpected end of file");
continue;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(cmd == "order_gain")
2016-03-14 09:04:03 -07:00
{
std::size_t curgain{0u};
2018-11-04 15:24:24 -08:00
float value;
while(istr.good())
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
istr >> value;
if(istr.fail()) break;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on gain "+std::to_string(curgain+1)+": "+
buffer.substr(static_cast<std::size_t>(istr.tellg())));
if(curgain < size(gains))
2018-11-04 15:24:24 -08:00
gains[curgain++] = value;
2016-03-14 09:04:03 -07:00
}
std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f);
2018-11-04 15:24:24 -08:00
gotgains = true;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(cmd == "add_row")
2016-03-14 09:04:03 -07:00
{
AmbDecConf::CoeffArray &mtxrow = matrix[cur++];
std::size_t curidx{0u};
float value{};
2018-11-04 15:24:24 -08:00
while(istr.good())
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
istr >> value;
if(istr.fail()) break;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
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;
2016-03-14 09:04:03 -07:00
}
std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f);
2016-03-14 09:04:03 -07:00
}
else
2020-12-25 09:26:24 -08:00
return al::make_optional("Unexpected matrix command: "+cmd);
2016-03-14 09:04:03 -07:00
2018-11-04 15:24:24 -08:00
istr.clear();
2018-12-12 21:18:31 -08:00
const auto endpos = static_cast<std::size_t>(istr.tellg());
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
2018-11-04 15:24:24 -08:00
buffer.clear();
2016-03-14 09:04:03 -07:00
}
if(!gotgains)
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Matrix order_gain not specified");
return al::nullopt;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
} // namespace
AmbDecConf::~AmbDecConf() = default;
2020-12-25 09:26:24 -08:00
al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
al::ifstream f{fname};
if(!f.is_open())
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Failed to open file");
2016-03-14 09:04:03 -07:00
bool speakers_loaded{false};
bool matrix_loaded{false};
bool lfmatrix_loaded{false};
2018-11-04 15:24:24 -08:00
std::string buffer;
while(read_clipped_line(f, buffer))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
std::istringstream istr{buffer};
2016-03-14 09:04:03 -07:00
std::string command{read_word(istr)};
2018-11-04 15:24:24 -08:00
if(command.empty())
2020-12-25 09:26:24 -08:00
return al::make_optional("Malformed line: "+buffer);
2016-03-14 09:04:03 -07:00
2018-11-04 15:24:24 -08:00
if(command == "/description")
readline(istr, Description);
2018-11-04 15:24:24 -08:00
else if(command == "/version")
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
istr >> Version;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk after version: " +
buffer.substr(static_cast<std::size_t>(istr.tellg())));
2018-11-03 19:51:23 -07:00
if(Version != 3)
2020-12-25 09:26:24 -08:00
return al::make_optional("Unsupported version: "+std::to_string(Version));
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(command == "/dec/chan_mask")
2016-03-14 09:04:03 -07:00
{
if(ChanMask)
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Duplicate chan_mask definition");
2018-11-04 15:24:24 -08:00
istr >> std::hex >> ChanMask >> std::dec;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk after mask: " +
buffer.substr(static_cast<std::size_t>(istr.tellg())));
if(!ChanMask)
2020-12-25 09:26:24 -08:00
return al::make_optional("Invalid chan_mask: "+std::to_string(ChanMask));
2018-11-04 15:24:24 -08:00
}
else if(command == "/dec/freq_bands")
{
if(FreqBands)
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Duplicate freq_bands");
2018-11-04 15:24:24 -08:00
istr >> FreqBands;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk after freq_bands: " +
buffer.substr(static_cast<std::size_t>(istr.tellg())));
2018-11-04 15:24:24 -08:00
if(FreqBands != 1 && FreqBands != 2)
2020-12-25 09:26:24 -08:00
return al::make_optional("Invalid freq_bands: "+std::to_string(FreqBands));
2018-11-04 15:24:24 -08:00
}
else if(command == "/dec/speakers")
{
2020-12-25 09:26:24 -08:00
if(NumSpeakers)
return al::make_optional<std::string>("Duplicate speakers");
istr >> NumSpeakers;
2018-11-04 15:24:24 -08:00
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
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));
Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers);
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(command == "/dec/coeff_scale")
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
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
2020-12-25 09:26:24 -08:00
return al::make_optional("Unexpected coeff_scale: "+scale);
2018-11-04 15:24:24 -08:00
}
else if(command == "/opt/xover_freq")
{
istr >> XOverFreq;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk after xover_freq: " +
buffer.substr(static_cast<std::size_t>(istr.tellg())));
2018-11-04 15:24:24 -08:00
}
else if(command == "/opt/xover_ratio")
{
istr >> XOverRatio;
if(!istr.eof() && !std::isspace(istr.peek()))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk after xover_ratio: " +
buffer.substr(static_cast<std::size_t>(istr.tellg())));
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" ||
command == "/opt/delay_comp" || command == "/opt/level_comp")
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
/* Unused */
read_word(istr);
}
else if(command == "/speakers/{")
{
2020-12-25 09:26:24 -08:00
if(!NumSpeakers)
return al::make_optional<std::string>("Speakers defined without a count");
2018-12-12 21:18:31 -08:00
const auto endpos = static_cast<std::size_t>(istr.tellg());
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
2018-11-04 15:24:24 -08:00
buffer.clear();
2020-12-25 09:26:24 -08:00
if(auto err = load_ambdec_speakers(Speakers.get(), NumSpeakers, f, buffer))
return err;
speakers_loaded = true;
2018-11-04 15:24:24 -08:00
if(!read_clipped_line(f, buffer))
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Unexpected end of file");
std::istringstream istr2{buffer};
std::string endmark{read_word(istr2)};
2018-11-04 15:24:24 -08:00
if(endmark != "/}")
2020-12-25 09:26:24 -08:00
return al::make_optional("Expected /} after speaker definitions, got "+endmark);
istr.swap(istr2);
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
2016-03-14 09:04:03 -07:00
{
if(!NumSpeakers)
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Matrix defined without a count");
2018-12-12 21:18:31 -08:00
const auto endpos = static_cast<std::size_t>(istr.tellg());
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
2018-11-04 15:24:24 -08:00
buffer.clear();
if(!Matrix)
{
Matrix = std::make_unique<CoeffArray[]>(NumSpeakers * FreqBands);
LFMatrix = Matrix.get();
HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1);
}
2018-11-03 19:51:23 -07:00
if(FreqBands == 1)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
if(command != "/matrix/{")
2020-12-25 09:26:24 -08:00
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;
2016-03-14 09:04:03 -07:00
}
else
{
2018-11-04 15:24:24 -08:00
if(command == "/lfmatrix/{")
2016-03-14 09:04:03 -07:00
{
2020-12-25 09:26:24 -08:00
if(auto err=load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer))
return err;
lfmatrix_loaded = true;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(command == "/hfmatrix/{")
2016-03-14 09:04:03 -07:00
{
2020-12-25 09:26:24 -08:00
if(auto err=load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
return err;
matrix_loaded = true;
2016-03-14 09:04:03 -07:00
}
else
2020-12-25 09:26:24 -08:00
return al::make_optional(
"Unexpected \""+command+"\" type for a dual-band decoder");
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(!read_clipped_line(f, buffer))
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Unexpected end of file");
std::istringstream istr2{buffer};
std::string endmark{read_word(istr2)};
2018-11-04 15:24:24 -08:00
if(endmark != "/}")
2020-12-25 09:26:24 -08:00
return al::make_optional("Expected /} after matrix definitions, got "+endmark);
istr.swap(istr2);
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
else if(command == "/end")
2016-03-14 09:04:03 -07:00
{
2018-12-12 21:18:31 -08:00
const auto endpos = static_cast<std::size_t>(istr.tellg());
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on end: " + buffer.substr(endpos));
2016-03-14 09:04:03 -07:00
if(!speakers_loaded || !matrix_loaded || (FreqBands == 2 && !lfmatrix_loaded))
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("No decoder defined");
2020-12-25 09:26:24 -08:00
return al::nullopt;
2016-03-14 09:04:03 -07:00
}
else
2020-12-25 09:26:24 -08:00
return al::make_optional("Unexpected command: " + command);
2016-03-14 09:04:03 -07:00
2018-11-04 15:24:24 -08:00
istr.clear();
2018-12-12 21:18:31 -08:00
const auto endpos = static_cast<std::size_t>(istr.tellg());
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2020-12-25 09:26:24 -08:00
return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
2018-11-04 15:24:24 -08:00
buffer.clear();
2016-03-14 09:04:03 -07:00
}
2020-12-25 09:26:24 -08:00
return al::make_optional<std::string>("Unexpected end of file");
2016-03-14 09:04:03 -07:00
}