openal-soft/Alc/ambdec.cpp

425 lines
12 KiB
C++
Raw Normal View History

2016-03-14 09:04:03 -07:00
#include "config.h"
#include "ambdec.h"
2018-11-04 15:24:24 -08:00
#include <cstring>
#include <cctype>
2016-03-14 09:04:03 -07:00
2018-11-04 15:24:24 -08:00
#include <limits>
#include <string>
#include <fstream>
#include <sstream>
2018-11-03 19:51:23 -07:00
2016-03-14 09:04:03 -07:00
#include "compat.h"
2018-11-04 15:24:24 -08:00
namespace {
2016-03-14 09:04:03 -07:00
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;
if(endpos < buffer.length())
return false;
return true;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
bool load_ambdec_speakers(AmbDecConf *conf, std::istream &f, std::string &buffer)
2016-03-14 09:04:03 -07:00
{
ALsizei cur = 0;
2016-03-14 09:04:03 -07:00
while(cur < conf->NumSpeakers)
{
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))
2016-03-14 09:04:03 -07:00
{
ERR("Unexpected end of file\n");
2018-11-04 15:24:24 -08:00
return false;
2016-03-14 09:04:03 -07:00
}
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
{
2018-11-04 15:24:24 -08:00
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);
2016-03-14 09:04:03 -07:00
cur++;
}
else
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected speakers command: %s\n", cmd.c_str());
return false;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
istr.clear();
std::istream::pos_type endpos{istr.tellg()};
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
return false;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
buffer.clear();
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
return true;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
bool load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, std::istream &f, std::string &buffer)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
bool gotgains = false;
ALsizei cur = 0;
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))
2016-03-14 09:04:03 -07:00
{
ERR("Unexpected end of file\n");
2018-11-04 15:24:24 -08:00
return false;
2016-03-14 09:04:03 -07:00
}
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
{
ALuint curgain = 0;
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()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk on gain %u: %s\n", curgain+1, buffer.c_str()+istr.tellg());
return false;
2016-03-14 09:04:03 -07:00
}
if(curgain < MAX_AMBI_ORDER+1)
2018-11-04 15:24:24 -08:00
gains[curgain++] = value;
2016-03-14 09:04:03 -07:00
}
while(curgain < MAX_AMBI_ORDER+1)
gains[curgain++] = 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
{
ALuint curidx = 0;
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()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx,
buffer.c_str()+istr.tellg());
return false;
2016-03-14 09:04:03 -07:00
}
if(curidx < MAX_AMBI_COEFFS)
2018-11-04 15:24:24 -08:00
matrix[cur][curidx++] = value;
2016-03-14 09:04:03 -07:00
}
while(curidx < MAX_AMBI_COEFFS)
matrix[cur][curidx++] = 0.0f;
cur++;
}
else
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected matrix command: %s\n", cmd.c_str());
return false;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
istr.clear();
std::istream::pos_type endpos{istr.tellg()};
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
return false;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
buffer.clear();
2016-03-14 09:04:03 -07:00
}
if(!gotgains)
{
ERR("Matrix order_gain not specified\n");
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
return true;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
} // namespace
int 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())
2016-03-14 09:04:03 -07:00
{
ERR("Failed to open: %s\n", fname);
return 0;
}
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())
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Malformed line: %s\n", buffer.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(command == "/description")
istr >> Description;
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()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk after version: %s\n", buffer.c_str()+istr.tellg());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-03 19:51:23 -07:00
if(Version != 3)
2016-03-14 09:04:03 -07:00
{
2018-11-03 19:51:23 -07:00
ERR("Unsupported version: %u\n", Version);
2018-11-04 15:24:24 -08:00
return 0;
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
{
2018-11-04 15:24:24 -08:00
istr >> std::hex >> ChanMask >> std::dec;
if(!istr.eof() && !std::isspace(istr.peek()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk after mask: %s\n", buffer.c_str()+istr.tellg());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
}
else if(command == "/dec/freq_bands")
{
istr >> FreqBands;
if(!istr.eof() && !std::isspace(istr.peek()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk after freq_bands: %s\n", buffer.c_str()+istr.tellg());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(FreqBands != 1 && FreqBands != 2)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Invalid freq_bands value: %u\n", FreqBands);
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
}
else if(command == "/dec/speakers")
{
istr >> NumSpeakers;
if(!istr.eof() && !std::isspace(istr.peek()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk after speakers: %s\n", buffer.c_str()+istr.tellg());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(NumSpeakers > MAX_OUTPUT_CHANNELS)
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unsupported speaker count: %u\n", NumSpeakers);
return 0;
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
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unsupported coeff scale: %s\n", scale.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
}
else if(command == "/opt/xover_freq")
{
istr >> XOverFreq;
if(!istr.eof() && !std::isspace(istr.peek()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk after xover_freq: %s\n", buffer.c_str()+istr.tellg());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
}
else if(command == "/opt/xover_ratio")
{
istr >> XOverRatio;
if(!istr.eof() && !std::isspace(istr.peek()))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Extra junk after xover_ratio: %s\n", buffer.c_str()+istr.tellg());
return 0;
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/{")
{
std::istream::pos_type endpos{istr.tellg()};
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
buffer.clear();
if(!load_ambdec_speakers(this, f, buffer))
return 0;
if(!read_clipped_line(f, buffer))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected end of file\n");
return 0;
2016-03-14 09:04:03 -07:00
}
std::istringstream istr2{buffer};
std::string endmark{read_word(istr2)};
2018-11-04 15:24:24 -08:00
if(endmark != "/}")
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
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
{
std::istream::pos_type endpos{istr.tellg()};
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
buffer.clear();
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/{")
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
if(!load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
return 0;
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
{
2018-11-04 15:24:24 -08:00
if(!load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer))
return 0;
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
{
2018-11-04 15:24:24 -08:00
if(!load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
return 0;
2016-03-14 09:04:03 -07:00
}
else
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
}
2018-11-04 15:24:24 -08:00
if(!read_clipped_line(f, buffer))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected end of file\n");
return 0;
2016-03-14 09:04:03 -07:00
}
std::istringstream istr2{buffer};
std::string endmark{read_word(istr2)};
2018-11-04 15:24:24 -08:00
if(endmark != "/}")
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
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
{
std::istream::pos_type endpos{istr.tellg()};
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos);
return 0;
2016-03-14 09:04:03 -07:00
}
return 1;
}
else
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected command: %s\n", command.c_str());
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
istr.clear();
std::istream::pos_type endpos{istr.tellg()};
2018-11-04 15:24:24 -08:00
if(!is_at_end(buffer, endpos))
2016-03-14 09:04:03 -07:00
{
2018-11-04 15:24:24 -08:00
ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
return 0;
2016-03-14 09:04:03 -07:00
}
2018-11-04 15:24:24 -08:00
buffer.clear();
2016-03-14 09:04:03 -07:00
}
ERR("Unexpected end of file\n");
return 0;
}