🛠️🐜 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.

436 lines
13 KiB

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