Use unique_ptr for BFormatDec and AmbiUpsampler
[openal-soft.git] / Alc / ambdec.cpp
blobc408a2aff53d5f5713b9a7065f819972b91e9f22
2 #include "config.h"
4 #include "ambdec.h"
6 #include <cstring>
7 #include <cctype>
9 #include <limits>
10 #include <string>
11 #include <fstream>
12 #include <sstream>
14 #include "compat.h"
17 namespace {
19 int readline(std::istream &f, std::string &output)
21 while(f.good() && f.peek() == '\n')
22 f.ignore();
24 return std::getline(f, output) && !output.empty();
27 bool read_clipped_line(std::istream &f, std::string &buffer)
29 while(readline(f, buffer))
31 std::size_t pos{0};
32 while(pos < buffer.length() && std::isspace(buffer[pos]))
33 pos++;
34 buffer.erase(0, pos);
36 std::size_t cmtpos{buffer.find_first_of('#')};
37 if(cmtpos < buffer.length())
38 buffer.resize(cmtpos);
39 while(!buffer.empty() && std::isspace(buffer.back()))
40 buffer.pop_back();
42 if(!buffer.empty())
43 return true;
45 return false;
49 std::string read_word(std::istream &f)
51 std::string ret;
52 f >> ret;
53 return ret;
56 bool is_at_end(const std::string &buffer, std::size_t endpos)
58 while(endpos < buffer.length() && std::isspace(buffer[endpos]))
59 ++endpos;
60 if(endpos < buffer.length())
61 return false;
62 return true;
66 bool load_ambdec_speakers(AmbDecConf *conf, std::istream &f, std::string &buffer)
68 ALsizei cur = 0;
69 while(cur < conf->NumSpeakers)
71 std::istringstream istr{buffer};
73 std::string cmd{read_word(istr)};
74 if(cmd.empty())
76 if(!read_clipped_line(f, buffer))
78 ERR("Unexpected end of file\n");
79 return false;
81 continue;
84 if(cmd == "add_spkr")
86 istr >> conf->Speakers[cur].Name;
87 if(istr.fail()) WARN("Name not specified for speaker %u\n", cur+1);
88 istr >> conf->Speakers[cur].Distance;
89 if(istr.fail()) WARN("Distance not specified for speaker %u\n", cur+1);
90 istr >> conf->Speakers[cur].Azimuth;
91 if(istr.fail()) WARN("Azimuth not specified for speaker %u\n", cur+1);
92 istr >> conf->Speakers[cur].Elevation;
93 if(istr.fail()) WARN("Elevation not specified for speaker %u\n", cur+1);
94 istr >> conf->Speakers[cur].Connection;
95 if(istr.fail()) TRACE("Connection not specified for speaker %u\n", cur+1);
97 cur++;
99 else
101 ERR("Unexpected speakers command: %s\n", cmd.c_str());
102 return false;
105 istr.clear();
106 std::istream::pos_type endpos{istr.tellg()};
107 if(!is_at_end(buffer, endpos))
109 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
110 return false;
112 buffer.clear();
115 return true;
118 bool load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, std::istream &f, std::string &buffer)
120 bool gotgains = false;
121 ALsizei cur = 0;
122 while(cur < maxrow)
124 std::istringstream istr{buffer};
126 std::string cmd{read_word(istr)};
127 if(cmd.empty())
129 if(!read_clipped_line(f, buffer))
131 ERR("Unexpected end of file\n");
132 return false;
134 continue;
137 if(cmd == "order_gain")
139 ALuint curgain = 0;
140 float value;
141 while(istr.good())
143 istr >> value;
144 if(istr.fail()) break;
145 if(!istr.eof() && !std::isspace(istr.peek()))
147 ERR("Extra junk on gain %u: %s\n", curgain+1, buffer.c_str()+istr.tellg());
148 return false;
150 if(curgain < MAX_AMBI_ORDER+1)
151 gains[curgain++] = value;
153 while(curgain < MAX_AMBI_ORDER+1)
154 gains[curgain++] = 0.0f;
155 gotgains = true;
157 else if(cmd == "add_row")
159 ALuint curidx = 0;
160 float value;
161 while(istr.good())
163 istr >> value;
164 if(istr.fail()) break;
165 if(!istr.eof() && !std::isspace(istr.peek()))
167 ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx,
168 buffer.c_str()+istr.tellg());
169 return false;
171 if(curidx < MAX_AMBI_COEFFS)
172 matrix[cur][curidx++] = value;
174 while(curidx < MAX_AMBI_COEFFS)
175 matrix[cur][curidx++] = 0.0f;
176 cur++;
178 else
180 ERR("Unexpected matrix command: %s\n", cmd.c_str());
181 return false;
184 istr.clear();
185 std::istream::pos_type endpos{istr.tellg()};
186 if(!is_at_end(buffer, endpos))
188 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
189 return false;
191 buffer.clear();
194 if(!gotgains)
196 ERR("Matrix order_gain not specified\n");
197 return false;
200 return true;
203 } // namespace
205 int AmbDecConf::load(const char *fname) noexcept
207 al::ifstream f{fname};
208 if(!f.is_open())
210 ERR("Failed to open: %s\n", fname);
211 return 0;
214 std::string buffer;
215 while(read_clipped_line(f, buffer))
217 std::istringstream istr{buffer};
219 std::string command{read_word(istr)};
220 if(command.empty())
222 ERR("Malformed line: %s\n", buffer.c_str());
223 return 0;
226 if(command == "/description")
227 istr >> Description;
228 else if(command == "/version")
230 istr >> Version;
231 if(!istr.eof() && !std::isspace(istr.peek()))
233 ERR("Extra junk after version: %s\n", buffer.c_str()+istr.tellg());
234 return 0;
236 if(Version != 3)
238 ERR("Unsupported version: %u\n", Version);
239 return 0;
242 else if(command == "/dec/chan_mask")
244 istr >> std::hex >> ChanMask >> std::dec;
245 if(!istr.eof() && !std::isspace(istr.peek()))
247 ERR("Extra junk after mask: %s\n", buffer.c_str()+istr.tellg());
248 return 0;
251 else if(command == "/dec/freq_bands")
253 istr >> FreqBands;
254 if(!istr.eof() && !std::isspace(istr.peek()))
256 ERR("Extra junk after freq_bands: %s\n", buffer.c_str()+istr.tellg());
257 return 0;
259 if(FreqBands != 1 && FreqBands != 2)
261 ERR("Invalid freq_bands value: %u\n", FreqBands);
262 return 0;
265 else if(command == "/dec/speakers")
267 istr >> NumSpeakers;
268 if(!istr.eof() && !std::isspace(istr.peek()))
270 ERR("Extra junk after speakers: %s\n", buffer.c_str()+istr.tellg());
271 return 0;
273 if(NumSpeakers > MAX_OUTPUT_CHANNELS)
275 ERR("Unsupported speaker count: %u\n", NumSpeakers);
276 return 0;
279 else if(command == "/dec/coeff_scale")
281 std::string scale = read_word(istr);
282 if(scale == "n3d") CoeffScale = AmbDecScale::N3D;
283 else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
284 else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
285 else
287 ERR("Unsupported coeff scale: %s\n", scale.c_str());
288 return 0;
291 else if(command == "/opt/xover_freq")
293 istr >> XOverFreq;
294 if(!istr.eof() && !std::isspace(istr.peek()))
296 ERR("Extra junk after xover_freq: %s\n", buffer.c_str()+istr.tellg());
297 return 0;
300 else if(command == "/opt/xover_ratio")
302 istr >> XOverRatio;
303 if(!istr.eof() && !std::isspace(istr.peek()))
305 ERR("Extra junk after xover_ratio: %s\n", buffer.c_str()+istr.tellg());
306 return 0;
309 else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" ||
310 command == "/opt/delay_comp" || command == "/opt/level_comp")
312 /* Unused */
313 read_word(istr);
315 else if(command == "/speakers/{")
317 std::istream::pos_type endpos{istr.tellg()};
318 if(!is_at_end(buffer, endpos))
320 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
321 return 0;
323 buffer.clear();
325 if(!load_ambdec_speakers(this, f, buffer))
326 return 0;
328 if(!read_clipped_line(f, buffer))
330 ERR("Unexpected end of file\n");
331 return 0;
333 std::istringstream istr2{buffer};
334 std::string endmark{read_word(istr2)};
335 if(endmark != "/}")
337 ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str());
338 return 0;
340 istr.swap(istr2);
342 else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
344 std::istream::pos_type endpos{istr.tellg()};
345 if(!is_at_end(buffer, endpos))
347 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
348 return 0;
350 buffer.clear();
352 if(FreqBands == 1)
354 if(command != "/matrix/{")
356 ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str());
357 return 0;
359 if(!load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
360 return 0;
362 else
364 if(command == "/lfmatrix/{")
366 if(!load_ambdec_matrix(LFOrderGain, LFMatrix, NumSpeakers, f, buffer))
367 return 0;
369 else if(command == "/hfmatrix/{")
371 if(!load_ambdec_matrix(HFOrderGain, HFMatrix, NumSpeakers, f, buffer))
372 return 0;
374 else
376 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str());
377 return 0;
381 if(!read_clipped_line(f, buffer))
383 ERR("Unexpected end of file\n");
384 return 0;
386 std::istringstream istr2{buffer};
387 std::string endmark{read_word(istr2)};
388 if(endmark != "/}")
390 ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str());
391 return 0;
393 istr.swap(istr2);
395 else if(command == "/end")
397 std::istream::pos_type endpos{istr.tellg()};
398 if(!is_at_end(buffer, endpos))
400 ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos);
401 return 0;
404 return 1;
406 else
408 ERR("Unexpected command: %s\n", command.c_str());
409 return 0;
412 istr.clear();
413 std::istream::pos_type endpos{istr.tellg()};
414 if(!is_at_end(buffer, endpos))
416 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
417 return 0;
419 buffer.clear();
421 ERR("Unexpected end of file\n");
423 return 0;