🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
13 KiB

  1. #include "config.h"
  2. #include "ambdec.h"
  3. #include <algorithm>
  4. #include <cctype>
  5. #include <cstddef>
  6. #include <iterator>
  7. #include <sstream>
  8. #include <string>
  9. #include "alfstream.h"
  10. #include "core/logging.h"
  11. namespace {
  12. template<typename T, std::size_t N>
  13. constexpr inline std::size_t size(const T(&)[N]) noexcept
  14. { return N; }
  15. int readline(std::istream &f, std::string &output)
  16. {
  17. while(f.good() && f.peek() == '\n')
  18. f.ignore();
  19. return std::getline(f, output) && !output.empty();
  20. }
  21. bool read_clipped_line(std::istream &f, std::string &buffer)
  22. {
  23. while(readline(f, buffer))
  24. {
  25. std::size_t pos{0};
  26. while(pos < buffer.length() && std::isspace(buffer[pos]))
  27. pos++;
  28. buffer.erase(0, pos);
  29. std::size_t cmtpos{buffer.find_first_of('#')};
  30. if(cmtpos < buffer.length())
  31. buffer.resize(cmtpos);
  32. while(!buffer.empty() && std::isspace(buffer.back()))
  33. buffer.pop_back();
  34. if(!buffer.empty())
  35. return true;
  36. }
  37. return false;
  38. }
  39. std::string read_word(std::istream &f)
  40. {
  41. std::string ret;
  42. f >> ret;
  43. return ret;
  44. }
  45. bool is_at_end(const std::string &buffer, std::size_t endpos)
  46. {
  47. while(endpos < buffer.length() && std::isspace(buffer[endpos]))
  48. ++endpos;
  49. return !(endpos < buffer.length());
  50. }
  51. al::optional<std::string> load_ambdec_speakers(AmbDecConf::SpeakerConf *spkrs,
  52. const std::size_t num_speakers, std::istream &f, std::string &buffer)
  53. {
  54. size_t cur_speaker{0};
  55. while(cur_speaker < num_speakers)
  56. {
  57. std::istringstream istr{buffer};
  58. std::string cmd{read_word(istr)};
  59. if(cmd.empty())
  60. {
  61. if(!read_clipped_line(f, buffer))
  62. return al::make_optional<std::string>("Unexpected end of file");
  63. continue;
  64. }
  65. if(cmd == "add_spkr")
  66. {
  67. AmbDecConf::SpeakerConf &spkr = spkrs[cur_speaker++];
  68. const size_t spkr_num{cur_speaker};
  69. istr >> spkr.Name;
  70. if(istr.fail()) WARN("Name not specified for speaker %zu\n", spkr_num);
  71. istr >> spkr.Distance;
  72. if(istr.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num);
  73. istr >> spkr.Azimuth;
  74. if(istr.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num);
  75. istr >> spkr.Elevation;
  76. if(istr.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num);
  77. istr >> spkr.Connection;
  78. if(istr.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num);
  79. }
  80. else
  81. return al::make_optional("Unexpected speakers command: "+cmd);
  82. istr.clear();
  83. const auto endpos = static_cast<std::size_t>(istr.tellg());
  84. if(!is_at_end(buffer, endpos))
  85. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  86. buffer.clear();
  87. }
  88. return al::nullopt;
  89. }
  90. al::optional<std::string> load_ambdec_matrix(float (&gains)[MaxAmbiOrder+1],
  91. AmbDecConf::CoeffArray *matrix, const std::size_t maxrow, std::istream &f, std::string &buffer)
  92. {
  93. bool gotgains{false};
  94. std::size_t cur{0u};
  95. while(cur < maxrow)
  96. {
  97. std::istringstream istr{buffer};
  98. std::string cmd{read_word(istr)};
  99. if(cmd.empty())
  100. {
  101. if(!read_clipped_line(f, buffer))
  102. return al::make_optional<std::string>("Unexpected end of file");
  103. continue;
  104. }
  105. if(cmd == "order_gain")
  106. {
  107. std::size_t curgain{0u};
  108. float value;
  109. while(istr.good())
  110. {
  111. istr >> value;
  112. if(istr.fail()) break;
  113. if(!istr.eof() && !std::isspace(istr.peek()))
  114. return al::make_optional("Extra junk on gain "+std::to_string(curgain+1)+": "+
  115. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  116. if(curgain < size(gains))
  117. gains[curgain++] = value;
  118. }
  119. std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f);
  120. gotgains = true;
  121. }
  122. else if(cmd == "add_row")
  123. {
  124. AmbDecConf::CoeffArray &mtxrow = matrix[cur++];
  125. std::size_t curidx{0u};
  126. float value{};
  127. while(istr.good())
  128. {
  129. istr >> value;
  130. if(istr.fail()) break;
  131. if(!istr.eof() && !std::isspace(istr.peek()))
  132. return al::make_optional("Extra junk on matrix element "+
  133. std::to_string(curidx)+"x"+std::to_string(cur-1)+": "+
  134. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  135. if(curidx < mtxrow.size())
  136. mtxrow[curidx++] = value;
  137. }
  138. std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f);
  139. }
  140. else
  141. return al::make_optional("Unexpected matrix command: "+cmd);
  142. istr.clear();
  143. const auto endpos = static_cast<std::size_t>(istr.tellg());
  144. if(!is_at_end(buffer, endpos))
  145. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  146. buffer.clear();
  147. }
  148. if(!gotgains)
  149. return al::make_optional<std::string>("Matrix order_gain not specified");
  150. return al::nullopt;
  151. }
  152. } // namespace
  153. AmbDecConf::~AmbDecConf() = default;
  154. al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
  155. {
  156. al::ifstream f{fname};
  157. if(!f.is_open())
  158. return al::make_optional<std::string>("Failed to open file");
  159. bool speakers_loaded{false};
  160. bool matrix_loaded{false};
  161. bool lfmatrix_loaded{false};
  162. std::string buffer;
  163. while(read_clipped_line(f, buffer))
  164. {
  165. std::istringstream istr{buffer};
  166. std::string command{read_word(istr)};
  167. if(command.empty())
  168. return al::make_optional("Malformed line: "+buffer);
  169. if(command == "/description")
  170. readline(istr, Description);
  171. else if(command == "/version")
  172. {
  173. istr >> Version;
  174. if(!istr.eof() && !std::isspace(istr.peek()))
  175. return al::make_optional("Extra junk after version: " +
  176. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  177. if(Version != 3)
  178. return al::make_optional("Unsupported version: "+std::to_string(Version));
  179. }
  180. else if(command == "/dec/chan_mask")
  181. {
  182. if(ChanMask)
  183. return al::make_optional<std::string>("Duplicate chan_mask definition");
  184. istr >> std::hex >> ChanMask >> std::dec;
  185. if(!istr.eof() && !std::isspace(istr.peek()))
  186. return al::make_optional("Extra junk after mask: " +
  187. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  188. if(!ChanMask)
  189. return al::make_optional("Invalid chan_mask: "+std::to_string(ChanMask));
  190. }
  191. else if(command == "/dec/freq_bands")
  192. {
  193. if(FreqBands)
  194. return al::make_optional<std::string>("Duplicate freq_bands");
  195. istr >> FreqBands;
  196. if(!istr.eof() && !std::isspace(istr.peek()))
  197. return al::make_optional("Extra junk after freq_bands: " +
  198. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  199. if(FreqBands != 1 && FreqBands != 2)
  200. return al::make_optional("Invalid freq_bands: "+std::to_string(FreqBands));
  201. }
  202. else if(command == "/dec/speakers")
  203. {
  204. if(NumSpeakers)
  205. return al::make_optional<std::string>("Duplicate speakers");
  206. istr >> NumSpeakers;
  207. if(!istr.eof() && !std::isspace(istr.peek()))
  208. return al::make_optional("Extra junk after speakers: " +
  209. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  210. if(!NumSpeakers)
  211. return al::make_optional("Invalid speakers: "+std::to_string(NumSpeakers));
  212. Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers);
  213. }
  214. else if(command == "/dec/coeff_scale")
  215. {
  216. std::string scale = read_word(istr);
  217. if(scale == "n3d") CoeffScale = AmbDecScale::N3D;
  218. else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
  219. else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
  220. else
  221. return al::make_optional("Unexpected coeff_scale: "+scale);
  222. }
  223. else if(command == "/opt/xover_freq")
  224. {
  225. istr >> XOverFreq;
  226. if(!istr.eof() && !std::isspace(istr.peek()))
  227. return al::make_optional("Extra junk after xover_freq: " +
  228. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  229. }
  230. else if(command == "/opt/xover_ratio")
  231. {
  232. istr >> XOverRatio;
  233. if(!istr.eof() && !std::isspace(istr.peek()))
  234. return al::make_optional("Extra junk after xover_ratio: " +
  235. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  236. }
  237. else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" ||
  238. command == "/opt/delay_comp" || command == "/opt/level_comp")
  239. {
  240. /* Unused */
  241. read_word(istr);
  242. }
  243. else if(command == "/speakers/{")
  244. {
  245. if(!NumSpeakers)
  246. return al::make_optional<std::string>("Speakers defined without a count");
  247. const auto endpos = static_cast<std::size_t>(istr.tellg());
  248. if(!is_at_end(buffer, endpos))
  249. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  250. buffer.clear();
  251. if(auto err = load_ambdec_speakers(Speakers.get(), NumSpeakers, f, buffer))
  252. return err;
  253. speakers_loaded = true;
  254. if(!read_clipped_line(f, buffer))
  255. return al::make_optional<std::string>("Unexpected end of file");
  256. std::istringstream istr2{buffer};
  257. std::string endmark{read_word(istr2)};
  258. if(endmark != "/}")
  259. return al::make_optional("Expected /} after speaker definitions, got "+endmark);
  260. istr.swap(istr2);
  261. }
  262. else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
  263. {
  264. if(!NumSpeakers)
  265. return al::make_optional<std::string>("Matrix defined without a count");
  266. const auto endpos = static_cast<std::size_t>(istr.tellg());
  267. if(!is_at_end(buffer, endpos))
  268. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  269. buffer.clear();
  270. if(!Matrix)
  271. {
  272. Matrix = std::make_unique<CoeffArray[]>(NumSpeakers * FreqBands);
  273. LFMatrix = Matrix.get();
  274. HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1);
  275. }
  276. if(FreqBands == 1)
  277. {
  278. if(command != "/matrix/{")
  279. return al::make_optional(
  280. "Unexpected \""+command+"\" type for a single-band decoder");
  281. if(auto err = load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
  282. return err;
  283. matrix_loaded = true;
  284. }
  285. else
  286. {
  287. if(command == "/lfmatrix/{")
  288. {
  289. if(auto err=load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer))
  290. return err;
  291. lfmatrix_loaded = true;
  292. }
  293. else if(command == "/hfmatrix/{")
  294. {
  295. if(auto err=load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
  296. return err;
  297. matrix_loaded = true;
  298. }
  299. else
  300. return al::make_optional(
  301. "Unexpected \""+command+"\" type for a dual-band decoder");
  302. }
  303. if(!read_clipped_line(f, buffer))
  304. return al::make_optional<std::string>("Unexpected end of file");
  305. std::istringstream istr2{buffer};
  306. std::string endmark{read_word(istr2)};
  307. if(endmark != "/}")
  308. return al::make_optional("Expected /} after matrix definitions, got "+endmark);
  309. istr.swap(istr2);
  310. }
  311. else if(command == "/end")
  312. {
  313. const auto endpos = static_cast<std::size_t>(istr.tellg());
  314. if(!is_at_end(buffer, endpos))
  315. return al::make_optional("Extra junk on end: " + buffer.substr(endpos));
  316. if(!speakers_loaded || !matrix_loaded || (FreqBands == 2 && !lfmatrix_loaded))
  317. return al::make_optional<std::string>("No decoder defined");
  318. return al::nullopt;
  319. }
  320. else
  321. return al::make_optional("Unexpected command: " + command);
  322. istr.clear();
  323. const auto endpos = static_cast<std::size_t>(istr.tellg());
  324. if(!is_at_end(buffer, endpos))
  325. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  326. buffer.clear();
  327. }
  328. return al::make_optional<std::string>("Unexpected end of file");
  329. }