From 20820fd01beb265722d8521ad725c3c479800273 Mon Sep 17 00:00:00 2001 From: Chris Robinson <chris.kcat@gmail.com> Date: Fri, 25 Dec 2020 06:30:47 -0800 Subject: Move the ambdec loader to core --- CMakeLists.txt | 4 +- alc/ambdec.cpp | 434 ----------------------------------------------------- alc/ambdec.h | 48 ------ alc/bformatdec.cpp | 2 +- alc/panning.cpp | 2 +- core/ambdec.cpp | 434 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/ambdec.h | 48 ++++++ 7 files changed, 486 insertions(+), 486 deletions(-) delete mode 100644 alc/ambdec.cpp delete mode 100644 alc/ambdec.h create mode 100644 core/ambdec.cpp create mode 100644 core/ambdec.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cbe7850..05d04201 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,6 +584,8 @@ set(COMMON_OBJS # Core library routines set(CORE_OBJS + core/ambdec.cpp + core/ambdec.h core/ambidefs.cpp core/ambidefs.h core/bs2b.cpp @@ -659,8 +661,6 @@ set(ALC_OBJS alc/alconfig.cpp alc/alconfig.h alc/alcontext.h - alc/ambdec.cpp - alc/ambdec.h alc/async_event.h alc/bformatdec.cpp alc/bformatdec.h diff --git a/alc/ambdec.cpp b/alc/ambdec.cpp deleted file mode 100644 index a50d6d95..00000000 --- a/alc/ambdec.cpp +++ /dev/null @@ -1,434 +0,0 @@ - -#include "config.h" - -#include "ambdec.h" - -#include <algorithm> -#include <cctype> -#include <cstddef> -#include <iterator> -#include <sstream> -#include <string> - -#include "alfstream.h" -#include "core/logging.h" - - -namespace { - -template<typename T, std::size_t N> -constexpr inline std::size_t size(const T(&)[N]) noexcept -{ return N; } - -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; - f >> ret; - return ret; -} - -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()); -} - - -bool load_ambdec_speakers(al::vector<AmbDecConf::SpeakerConf> &spkrs, const std::size_t num_speakers, std::istream &f, std::string &buffer) -{ - while(spkrs.size() < num_speakers) - { - std::istringstream istr{buffer}; - - std::string cmd{read_word(istr)}; - if(cmd.empty()) - { - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return false; - } - continue; - } - - if(cmd == "add_spkr") - { - spkrs.emplace_back(); - AmbDecConf::SpeakerConf &spkr = spkrs.back(); - const size_t spkr_num{spkrs.size()}; - - 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 - { - ERR("Unexpected speakers command: %s\n", cmd.c_str()); - return false; - } - - istr.clear(); - const auto endpos = static_cast<std::size_t>(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return false; - } - buffer.clear(); - } - - return true; -} - -bool load_ambdec_matrix(float (&gains)[MaxAmbiOrder+1], al::vector<AmbDecConf::CoeffArray> &matrix, const std::size_t maxrow, std::istream &f, std::string &buffer) -{ - bool gotgains{false}; - std::size_t cur{0u}; - while(cur < maxrow) - { - std::istringstream istr{buffer}; - - std::string cmd{read_word(istr)}; - if(cmd.empty()) - { - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return false; - } - continue; - } - - if(cmd == "order_gain") - { - std::size_t curgain{0u}; - float value; - while(istr.good()) - { - istr >> value; - if(istr.fail()) break; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk on gain %zu: %s\n", curgain+1, - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return false; - } - if(curgain < size(gains)) - gains[curgain++] = value; - } - std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f); - gotgains = true; - } - else if(cmd == "add_row") - { - matrix.emplace_back(); - AmbDecConf::CoeffArray &mtxrow = matrix.back(); - std::size_t curidx{0u}; - float value{}; - while(istr.good()) - { - istr >> value; - if(istr.fail()) break; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk on matrix element %zux%zu: %s\n", curidx, - matrix.size(), buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - matrix.pop_back(); - return false; - } - if(curidx < mtxrow.size()) - mtxrow[curidx++] = value; - } - std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f); - cur++; - } - else - { - ERR("Unexpected matrix command: %s\n", cmd.c_str()); - return false; - } - - istr.clear(); - const auto endpos = static_cast<std::size_t>(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return false; - } - buffer.clear(); - } - - if(!gotgains) - { - ERR("Matrix order_gain not specified\n"); - return false; - } - - return true; -} - -} // namespace - -int AmbDecConf::load(const char *fname) noexcept -{ - al::ifstream f{fname}; - if(!f.is_open()) - { - ERR("Failed to open: %s\n", fname); - return 0; - } - - std::size_t num_speakers{0u}; - std::string buffer; - while(read_clipped_line(f, buffer)) - { - std::istringstream istr{buffer}; - - std::string command{read_word(istr)}; - if(command.empty()) - { - ERR("Malformed line: %s\n", buffer.c_str()); - return 0; - } - - if(command == "/description") - istr >> Description; - else if(command == "/version") - { - istr >> Version; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after version: %s\n", - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return 0; - } - if(Version != 3) - { - ERR("Unsupported version: %u\n", Version); - return 0; - } - } - else if(command == "/dec/chan_mask") - { - istr >> std::hex >> ChanMask >> std::dec; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after mask: %s\n", - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return 0; - } - } - else if(command == "/dec/freq_bands") - { - istr >> FreqBands; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after freq_bands: %s\n", - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return 0; - } - if(FreqBands != 1 && FreqBands != 2) - { - ERR("Invalid freq_bands value: %u\n", FreqBands); - return 0; - } - } - else if(command == "/dec/speakers") - { - istr >> num_speakers; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after speakers: %s\n", - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return 0; - } - Speakers.reserve(num_speakers); - LFMatrix.reserve(num_speakers); - HFMatrix.reserve(num_speakers); - } - else if(command == "/dec/coeff_scale") - { - 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 - { - ERR("Unsupported coeff scale: %s\n", scale.c_str()); - return 0; - } - } - else if(command == "/opt/xover_freq") - { - istr >> XOverFreq; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after xover_freq: %s\n", - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return 0; - } - } - else if(command == "/opt/xover_ratio") - { - istr >> XOverRatio; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after xover_ratio: %s\n", - buffer.c_str()+static_cast<std::size_t>(istr.tellg())); - return 0; - } - } - else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" || - command == "/opt/delay_comp" || command == "/opt/level_comp") - { - /* Unused */ - read_word(istr); - } - else if(command == "/speakers/{") - { - const auto endpos = static_cast<std::size_t>(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return 0; - } - buffer.clear(); - - if(!load_ambdec_speakers(Speakers, num_speakers, f, buffer)) - return 0; - - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return 0; - } - std::istringstream istr2{buffer}; - std::string endmark{read_word(istr2)}; - if(endmark != "/}") - { - ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str()); - return 0; - } - istr.swap(istr2); - } - else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") - { - const auto endpos = static_cast<std::size_t>(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return 0; - } - buffer.clear(); - - if(FreqBands == 1) - { - if(command != "/matrix/{") - { - ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str()); - return 0; - } - if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) - return 0; - } - else - { - if(command == "/lfmatrix/{") - { - if(!load_ambdec_matrix(LFOrderGain, LFMatrix, num_speakers, f, buffer)) - return 0; - } - else if(command == "/hfmatrix/{") - { - if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) - return 0; - } - else - { - ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str()); - return 0; - } - } - - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return 0; - } - std::istringstream istr2{buffer}; - std::string endmark{read_word(istr2)}; - if(endmark != "/}") - { - ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str()); - return 0; - } - istr.swap(istr2); - } - else if(command == "/end") - { - const auto endpos = static_cast<std::size_t>(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos); - return 0; - } - - return 1; - } - else - { - ERR("Unexpected command: %s\n", command.c_str()); - return 0; - } - - istr.clear(); - const auto endpos = static_cast<std::size_t>(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return 0; - } - buffer.clear(); - } - ERR("Unexpected end of file\n"); - - return 0; -} diff --git a/alc/ambdec.h b/alc/ambdec.h deleted file mode 100644 index c96ded5b..00000000 --- a/alc/ambdec.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef AMBDEC_H -#define AMBDEC_H - -#include <array> -#include <string> - -#include "core/ambidefs.h" -#include "vector.h" - -/* Helpers to read .ambdec configuration files. */ - -enum class AmbDecScale { - N3D, - SN3D, - FuMa, -}; -struct AmbDecConf { - std::string Description; - int Version{0}; /* Must be 3 */ - - unsigned int ChanMask{0u}; - unsigned int FreqBands{0u}; /* Must be 1 or 2 */ - AmbDecScale CoeffScale{}; - - float XOverFreq{0.0f}; - float XOverRatio{0.0f}; - - struct SpeakerConf { - std::string Name; - float Distance{0.0f}; - float Azimuth{0.0f}; - float Elevation{0.0f}; - std::string Connection; - }; - al::vector<SpeakerConf> Speakers; - - using CoeffArray = std::array<float,MaxAmbiChannels>; - /* Unused when FreqBands == 1 */ - float LFOrderGain[MaxAmbiOrder+1]{}; - al::vector<CoeffArray> LFMatrix; - - float HFOrderGain[MaxAmbiOrder+1]{}; - al::vector<CoeffArray> HFMatrix; - - int load(const char *fname) noexcept; -}; - -#endif /* AMBDEC_H */ diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp index c47a042f..b1bd8981 100644 --- a/alc/bformatdec.cpp +++ b/alc/bformatdec.cpp @@ -12,7 +12,7 @@ #include "almalloc.h" #include "alu.h" -#include "ambdec.h" +#include "core/ambdec.h" #include "core/filters/splitter.h" #include "front_stablizer.h" #include "math_defs.h" diff --git a/alc/panning.cpp b/alc/panning.cpp index cb77199f..456f6bef 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -47,8 +47,8 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" -#include "ambdec.h" #include "bformatdec.h" +#include "core/ambdec.h" #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" diff --git a/core/ambdec.cpp b/core/ambdec.cpp new file mode 100644 index 00000000..a50d6d95 --- /dev/null +++ b/core/ambdec.cpp @@ -0,0 +1,434 @@ + +#include "config.h" + +#include "ambdec.h" + +#include <algorithm> +#include <cctype> +#include <cstddef> +#include <iterator> +#include <sstream> +#include <string> + +#include "alfstream.h" +#include "core/logging.h" + + +namespace { + +template<typename T, std::size_t N> +constexpr inline std::size_t size(const T(&)[N]) noexcept +{ return N; } + +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; + f >> ret; + return ret; +} + +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()); +} + + +bool load_ambdec_speakers(al::vector<AmbDecConf::SpeakerConf> &spkrs, const std::size_t num_speakers, std::istream &f, std::string &buffer) +{ + while(spkrs.size() < num_speakers) + { + std::istringstream istr{buffer}; + + std::string cmd{read_word(istr)}; + if(cmd.empty()) + { + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return false; + } + continue; + } + + if(cmd == "add_spkr") + { + spkrs.emplace_back(); + AmbDecConf::SpeakerConf &spkr = spkrs.back(); + const size_t spkr_num{spkrs.size()}; + + 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 + { + ERR("Unexpected speakers command: %s\n", cmd.c_str()); + return false; + } + + istr.clear(); + const auto endpos = static_cast<std::size_t>(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return false; + } + buffer.clear(); + } + + return true; +} + +bool load_ambdec_matrix(float (&gains)[MaxAmbiOrder+1], al::vector<AmbDecConf::CoeffArray> &matrix, const std::size_t maxrow, std::istream &f, std::string &buffer) +{ + bool gotgains{false}; + std::size_t cur{0u}; + while(cur < maxrow) + { + std::istringstream istr{buffer}; + + std::string cmd{read_word(istr)}; + if(cmd.empty()) + { + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return false; + } + continue; + } + + if(cmd == "order_gain") + { + std::size_t curgain{0u}; + float value; + while(istr.good()) + { + istr >> value; + if(istr.fail()) break; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk on gain %zu: %s\n", curgain+1, + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return false; + } + if(curgain < size(gains)) + gains[curgain++] = value; + } + std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f); + gotgains = true; + } + else if(cmd == "add_row") + { + matrix.emplace_back(); + AmbDecConf::CoeffArray &mtxrow = matrix.back(); + std::size_t curidx{0u}; + float value{}; + while(istr.good()) + { + istr >> value; + if(istr.fail()) break; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk on matrix element %zux%zu: %s\n", curidx, + matrix.size(), buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + matrix.pop_back(); + return false; + } + if(curidx < mtxrow.size()) + mtxrow[curidx++] = value; + } + std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f); + cur++; + } + else + { + ERR("Unexpected matrix command: %s\n", cmd.c_str()); + return false; + } + + istr.clear(); + const auto endpos = static_cast<std::size_t>(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return false; + } + buffer.clear(); + } + + if(!gotgains) + { + ERR("Matrix order_gain not specified\n"); + return false; + } + + return true; +} + +} // namespace + +int AmbDecConf::load(const char *fname) noexcept +{ + al::ifstream f{fname}; + if(!f.is_open()) + { + ERR("Failed to open: %s\n", fname); + return 0; + } + + std::size_t num_speakers{0u}; + std::string buffer; + while(read_clipped_line(f, buffer)) + { + std::istringstream istr{buffer}; + + std::string command{read_word(istr)}; + if(command.empty()) + { + ERR("Malformed line: %s\n", buffer.c_str()); + return 0; + } + + if(command == "/description") + istr >> Description; + else if(command == "/version") + { + istr >> Version; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after version: %s\n", + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return 0; + } + if(Version != 3) + { + ERR("Unsupported version: %u\n", Version); + return 0; + } + } + else if(command == "/dec/chan_mask") + { + istr >> std::hex >> ChanMask >> std::dec; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after mask: %s\n", + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return 0; + } + } + else if(command == "/dec/freq_bands") + { + istr >> FreqBands; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after freq_bands: %s\n", + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return 0; + } + if(FreqBands != 1 && FreqBands != 2) + { + ERR("Invalid freq_bands value: %u\n", FreqBands); + return 0; + } + } + else if(command == "/dec/speakers") + { + istr >> num_speakers; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after speakers: %s\n", + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return 0; + } + Speakers.reserve(num_speakers); + LFMatrix.reserve(num_speakers); + HFMatrix.reserve(num_speakers); + } + else if(command == "/dec/coeff_scale") + { + 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 + { + ERR("Unsupported coeff scale: %s\n", scale.c_str()); + return 0; + } + } + else if(command == "/opt/xover_freq") + { + istr >> XOverFreq; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after xover_freq: %s\n", + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return 0; + } + } + else if(command == "/opt/xover_ratio") + { + istr >> XOverRatio; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after xover_ratio: %s\n", + buffer.c_str()+static_cast<std::size_t>(istr.tellg())); + return 0; + } + } + else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" || + command == "/opt/delay_comp" || command == "/opt/level_comp") + { + /* Unused */ + read_word(istr); + } + else if(command == "/speakers/{") + { + const auto endpos = static_cast<std::size_t>(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return 0; + } + buffer.clear(); + + if(!load_ambdec_speakers(Speakers, num_speakers, f, buffer)) + return 0; + + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return 0; + } + std::istringstream istr2{buffer}; + std::string endmark{read_word(istr2)}; + if(endmark != "/}") + { + ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str()); + return 0; + } + istr.swap(istr2); + } + else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") + { + const auto endpos = static_cast<std::size_t>(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return 0; + } + buffer.clear(); + + if(FreqBands == 1) + { + if(command != "/matrix/{") + { + ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str()); + return 0; + } + if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) + return 0; + } + else + { + if(command == "/lfmatrix/{") + { + if(!load_ambdec_matrix(LFOrderGain, LFMatrix, num_speakers, f, buffer)) + return 0; + } + else if(command == "/hfmatrix/{") + { + if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) + return 0; + } + else + { + ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str()); + return 0; + } + } + + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return 0; + } + std::istringstream istr2{buffer}; + std::string endmark{read_word(istr2)}; + if(endmark != "/}") + { + ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str()); + return 0; + } + istr.swap(istr2); + } + else if(command == "/end") + { + const auto endpos = static_cast<std::size_t>(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos); + return 0; + } + + return 1; + } + else + { + ERR("Unexpected command: %s\n", command.c_str()); + return 0; + } + + istr.clear(); + const auto endpos = static_cast<std::size_t>(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return 0; + } + buffer.clear(); + } + ERR("Unexpected end of file\n"); + + return 0; +} diff --git a/core/ambdec.h b/core/ambdec.h new file mode 100644 index 00000000..9f0db6b5 --- /dev/null +++ b/core/ambdec.h @@ -0,0 +1,48 @@ +#ifndef CORE_AMBDEC_H +#define CORE_AMBDEC_H + +#include <array> +#include <string> + +#include "core/ambidefs.h" +#include "vector.h" + +/* Helpers to read .ambdec configuration files. */ + +enum class AmbDecScale { + N3D, + SN3D, + FuMa, +}; +struct AmbDecConf { + std::string Description; + int Version{0}; /* Must be 3 */ + + unsigned int ChanMask{0u}; + unsigned int FreqBands{0u}; /* Must be 1 or 2 */ + AmbDecScale CoeffScale{}; + + float XOverFreq{0.0f}; + float XOverRatio{0.0f}; + + struct SpeakerConf { + std::string Name; + float Distance{0.0f}; + float Azimuth{0.0f}; + float Elevation{0.0f}; + std::string Connection; + }; + al::vector<SpeakerConf> Speakers; + + using CoeffArray = std::array<float,MaxAmbiChannels>; + /* Unused when FreqBands == 1 */ + float LFOrderGain[MaxAmbiOrder+1]{}; + al::vector<CoeffArray> LFMatrix; + + float HFOrderGain[MaxAmbiOrder+1]{}; + al::vector<CoeffArray> HFMatrix; + + int load(const char *fname) noexcept; +}; + +#endif /* CORE_AMBDEC_H */ -- cgit v1.2.3