diff options
Diffstat (limited to 'Alc/ambdec.cpp')
-rw-r--r-- | Alc/ambdec.cpp | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/Alc/ambdec.cpp b/Alc/ambdec.cpp new file mode 100644 index 00000000..088d4a85 --- /dev/null +++ b/Alc/ambdec.cpp @@ -0,0 +1,425 @@ + +#include "config.h" + +#include "ambdec.h" + +#include <cstring> +#include <cctype> + +#include <limits> +#include <string> +#include <fstream> +#include <sstream> + +#include "compat.h" + + +namespace { + +int readline(std::istream &f, std::string &output) +{ + while(f.good() && f.peek() == '\n') + f.ignore(); + + std::getline(f, output); + return !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; + if(endpos < buffer.length()) + return false; + return true; +} + + +bool load_ambdec_speakers(AmbDecConf *conf, std::istream &f, std::string &buffer) +{ + ALsizei cur = 0; + while(cur < conf->NumSpeakers) + { + 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") + { + istr >> conf->Speakers[cur].Name; + if(istr.fail()) WARN("Name not specified for speaker %u\n", cur+1); + istr >> conf->Speakers[cur].Distance; + if(istr.fail()) WARN("Distance not specified for speaker %u\n", cur+1); + istr >> conf->Speakers[cur].Azimuth; + if(istr.fail()) WARN("Azimuth not specified for speaker %u\n", cur+1); + istr >> conf->Speakers[cur].Elevation; + if(istr.fail()) WARN("Elevation not specified for speaker %u\n", cur+1); + istr >> conf->Speakers[cur].Connection; + if(istr.fail()) TRACE("Connection not specified for speaker %u\n", cur+1); + + cur++; + } + else + { + ERR("Unexpected speakers command: %s\n", cmd.c_str()); + return false; + } + + istr.clear(); + std::istream::pos_type endpos{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(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, std::istream &f, std::string &buffer) +{ + bool gotgains = false; + ALsizei cur = 0; + 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") + { + ALuint curgain = 0; + float value; + while(istr.good()) + { + istr >> value; + if(istr.fail()) break; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk on gain %u: %s\n", curgain+1, buffer.c_str()+istr.tellg()); + return false; + } + if(curgain < MAX_AMBI_ORDER+1) + gains[curgain++] = value; + } + while(curgain < MAX_AMBI_ORDER+1) + gains[curgain++] = 0.0f; + gotgains = true; + } + else if(cmd == "add_row") + { + ALuint curidx = 0; + float value; + while(istr.good()) + { + istr >> value; + if(istr.fail()) break; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, + buffer.c_str()+istr.tellg()); + return false; + } + if(curidx < MAX_AMBI_COEFFS) + matrix[cur][curidx++] = value; + } + while(curidx < MAX_AMBI_COEFFS) + matrix[cur][curidx++] = 0.0f; + cur++; + } + else + { + ERR("Unexpected matrix command: %s\n", cmd.c_str()); + return false; + } + + istr.clear(); + std::istream::pos_type endpos{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) +{ + al::ifstream f{fname}; + if(!f.is_open()) + { + ERR("Failed to open: %s\n", fname); + return 0; + } + + 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()+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()+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()+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 >> NumSpeakers; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after speakers: %s\n", buffer.c_str()+istr.tellg()); + return 0; + } + if(NumSpeakers > MAX_OUTPUT_CHANNELS) + { + ERR("Unsupported speaker count: %u\n", NumSpeakers); + return 0; + } + } + 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()+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()+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/{") + { + std::istream::pos_type endpos{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(this, 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/{") + { + std::istream::pos_type endpos{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, NumSpeakers, f, buffer)) + return 0; + } + else + { + if(command == "/lfmatrix/{") + { + if(!load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer)) + return 0; + } + else if(command == "/hfmatrix/{") + { + if(!load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, 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") + { + std::istream::pos_type endpos{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(); + std::istream::pos_type endpos{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; +} |