ambdec.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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. al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
  154. {
  155. al::ifstream f{fname};
  156. if(!f.is_open())
  157. return al::make_optional<std::string>("Failed to open file");
  158. bool speakers_loaded{false};
  159. bool matrix_loaded{false};
  160. bool lfmatrix_loaded{false};
  161. std::string buffer;
  162. while(read_clipped_line(f, buffer))
  163. {
  164. std::istringstream istr{buffer};
  165. std::string command{read_word(istr)};
  166. if(command.empty())
  167. return al::make_optional("Malformed line: "+buffer);
  168. if(command == "/description")
  169. istr >> Description;
  170. else if(command == "/version")
  171. {
  172. istr >> Version;
  173. if(!istr.eof() && !std::isspace(istr.peek()))
  174. return al::make_optional("Extra junk after version: " +
  175. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  176. if(Version != 3)
  177. return al::make_optional("Unsupported version: "+std::to_string(Version));
  178. }
  179. else if(command == "/dec/chan_mask")
  180. {
  181. if(ChanMask)
  182. return al::make_optional<std::string>("Duplicate chan_mask definition");
  183. istr >> std::hex >> ChanMask >> std::dec;
  184. if(!istr.eof() && !std::isspace(istr.peek()))
  185. return al::make_optional("Extra junk after mask: " +
  186. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  187. if(!ChanMask)
  188. return al::make_optional("Invalid chan_mask: "+std::to_string(ChanMask));
  189. }
  190. else if(command == "/dec/freq_bands")
  191. {
  192. if(FreqBands)
  193. return al::make_optional<std::string>("Duplicate freq_bands");
  194. istr >> FreqBands;
  195. if(!istr.eof() && !std::isspace(istr.peek()))
  196. return al::make_optional("Extra junk after freq_bands: " +
  197. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  198. if(FreqBands != 1 && FreqBands != 2)
  199. return al::make_optional("Invalid freq_bands: "+std::to_string(FreqBands));
  200. }
  201. else if(command == "/dec/speakers")
  202. {
  203. if(NumSpeakers)
  204. return al::make_optional<std::string>("Duplicate speakers");
  205. istr >> NumSpeakers;
  206. if(!istr.eof() && !std::isspace(istr.peek()))
  207. return al::make_optional("Extra junk after speakers: " +
  208. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  209. if(!NumSpeakers)
  210. return al::make_optional("Invalid speakers: "+std::to_string(NumSpeakers));
  211. Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers);
  212. }
  213. else if(command == "/dec/coeff_scale")
  214. {
  215. std::string scale = read_word(istr);
  216. if(scale == "n3d") CoeffScale = AmbDecScale::N3D;
  217. else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
  218. else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
  219. else
  220. return al::make_optional("Unexpected coeff_scale: "+scale);
  221. }
  222. else if(command == "/opt/xover_freq")
  223. {
  224. istr >> XOverFreq;
  225. if(!istr.eof() && !std::isspace(istr.peek()))
  226. return al::make_optional("Extra junk after xover_freq: " +
  227. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  228. }
  229. else if(command == "/opt/xover_ratio")
  230. {
  231. istr >> XOverRatio;
  232. if(!istr.eof() && !std::isspace(istr.peek()))
  233. return al::make_optional("Extra junk after xover_ratio: " +
  234. buffer.substr(static_cast<std::size_t>(istr.tellg())));
  235. }
  236. else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" ||
  237. command == "/opt/delay_comp" || command == "/opt/level_comp")
  238. {
  239. /* Unused */
  240. read_word(istr);
  241. }
  242. else if(command == "/speakers/{")
  243. {
  244. if(!NumSpeakers)
  245. return al::make_optional<std::string>("Speakers defined without a count");
  246. const auto endpos = static_cast<std::size_t>(istr.tellg());
  247. if(!is_at_end(buffer, endpos))
  248. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  249. buffer.clear();
  250. if(auto err = load_ambdec_speakers(Speakers.get(), NumSpeakers, f, buffer))
  251. return err;
  252. speakers_loaded = true;
  253. if(!read_clipped_line(f, buffer))
  254. return al::make_optional<std::string>("Unexpected end of file");
  255. std::istringstream istr2{buffer};
  256. std::string endmark{read_word(istr2)};
  257. if(endmark != "/}")
  258. return al::make_optional("Expected /} after speaker definitions, got "+endmark);
  259. istr.swap(istr2);
  260. }
  261. else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
  262. {
  263. if(!NumSpeakers)
  264. return al::make_optional<std::string>("Matrix defined without a count");
  265. const auto endpos = static_cast<std::size_t>(istr.tellg());
  266. if(!is_at_end(buffer, endpos))
  267. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  268. buffer.clear();
  269. if(!Matrix)
  270. {
  271. Matrix = std::make_unique<CoeffArray[]>(NumSpeakers * FreqBands);
  272. LFMatrix = Matrix.get();
  273. HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1);
  274. }
  275. if(FreqBands == 1)
  276. {
  277. if(command != "/matrix/{")
  278. return al::make_optional(
  279. "Unexpected \""+command+"\" type for a single-band decoder");
  280. if(auto err = load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
  281. return err;
  282. matrix_loaded = true;
  283. }
  284. else
  285. {
  286. if(command == "/lfmatrix/{")
  287. {
  288. if(auto err=load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer))
  289. return err;
  290. lfmatrix_loaded = true;
  291. }
  292. else if(command == "/hfmatrix/{")
  293. {
  294. if(auto err=load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
  295. return err;
  296. matrix_loaded = true;
  297. }
  298. else
  299. return al::make_optional(
  300. "Unexpected \""+command+"\" type for a dual-band decoder");
  301. }
  302. if(!read_clipped_line(f, buffer))
  303. return al::make_optional<std::string>("Unexpected end of file");
  304. std::istringstream istr2{buffer};
  305. std::string endmark{read_word(istr2)};
  306. if(endmark != "/}")
  307. return al::make_optional("Expected /} after matrix definitions, got "+endmark);
  308. istr.swap(istr2);
  309. }
  310. else if(command == "/end")
  311. {
  312. const auto endpos = static_cast<std::size_t>(istr.tellg());
  313. if(!is_at_end(buffer, endpos))
  314. return al::make_optional("Extra junk on end: " + buffer.substr(endpos));
  315. if(!speakers_loaded || !matrix_loaded || (FreqBands == 2 && !lfmatrix_loaded))
  316. return al::make_optional<std::string>("No decoder defined");
  317. return al::nullopt;
  318. }
  319. else
  320. return al::make_optional("Unexpected command: " + command);
  321. istr.clear();
  322. const auto endpos = static_cast<std::size_t>(istr.tellg());
  323. if(!is_at_end(buffer, endpos))
  324. return al::make_optional("Extra junk on line: " + buffer.substr(endpos));
  325. buffer.clear();
  326. }
  327. return al::make_optional<std::string>("Unexpected end of file");
  328. }