Use one PulseAudio mainloop per device
[openal-soft.git] / alc / ambdec.cpp
blobadf116fe2c37bb415ada6531cf4f1e8a9776e6ec
2 #include "config.h"
4 #include "ambdec.h"
6 #include <algorithm>
7 #include <cctype>
8 #include <cstddef>
9 #include <iterator>
10 #include <sstream>
11 #include <string>
13 #include "alfstream.h"
14 #include "logging.h"
17 namespace {
19 template<typename T, std::size_t N>
20 constexpr inline std::size_t size(const T(&)[N]) noexcept
21 { return N; }
23 int readline(std::istream &f, std::string &output)
25 while(f.good() && f.peek() == '\n')
26 f.ignore();
28 return std::getline(f, output) && !output.empty();
31 bool read_clipped_line(std::istream &f, std::string &buffer)
33 while(readline(f, buffer))
35 std::size_t pos{0};
36 while(pos < buffer.length() && std::isspace(buffer[pos]))
37 pos++;
38 buffer.erase(0, pos);
40 std::size_t cmtpos{buffer.find_first_of('#')};
41 if(cmtpos < buffer.length())
42 buffer.resize(cmtpos);
43 while(!buffer.empty() && std::isspace(buffer.back()))
44 buffer.pop_back();
46 if(!buffer.empty())
47 return true;
49 return false;
53 std::string read_word(std::istream &f)
55 std::string ret;
56 f >> ret;
57 return ret;
60 bool is_at_end(const std::string &buffer, std::size_t endpos)
62 while(endpos < buffer.length() && std::isspace(buffer[endpos]))
63 ++endpos;
64 return !(endpos < buffer.length());
68 bool load_ambdec_speakers(al::vector<AmbDecConf::SpeakerConf> &spkrs, const std::size_t num_speakers, std::istream &f, std::string &buffer)
70 while(spkrs.size() < num_speakers)
72 std::istringstream istr{buffer};
74 std::string cmd{read_word(istr)};
75 if(cmd.empty())
77 if(!read_clipped_line(f, buffer))
79 ERR("Unexpected end of file\n");
80 return false;
82 continue;
85 if(cmd == "add_spkr")
87 spkrs.emplace_back();
88 AmbDecConf::SpeakerConf &spkr = spkrs.back();
89 const size_t spkr_num{spkrs.size()};
91 istr >> spkr.Name;
92 if(istr.fail()) WARN("Name not specified for speaker %zu\n", spkr_num);
93 istr >> spkr.Distance;
94 if(istr.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num);
95 istr >> spkr.Azimuth;
96 if(istr.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num);
97 istr >> spkr.Elevation;
98 if(istr.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num);
99 istr >> spkr.Connection;
100 if(istr.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num);
102 else
104 ERR("Unexpected speakers command: %s\n", cmd.c_str());
105 return false;
108 istr.clear();
109 const auto endpos = static_cast<std::size_t>(istr.tellg());
110 if(!is_at_end(buffer, endpos))
112 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
113 return false;
115 buffer.clear();
118 return true;
121 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)
123 bool gotgains{false};
124 std::size_t cur{0u};
125 while(cur < maxrow)
127 std::istringstream istr{buffer};
129 std::string cmd{read_word(istr)};
130 if(cmd.empty())
132 if(!read_clipped_line(f, buffer))
134 ERR("Unexpected end of file\n");
135 return false;
137 continue;
140 if(cmd == "order_gain")
142 std::size_t curgain{0u};
143 float value;
144 while(istr.good())
146 istr >> value;
147 if(istr.fail()) break;
148 if(!istr.eof() && !std::isspace(istr.peek()))
150 ERR("Extra junk on gain %zu: %s\n", curgain+1,
151 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
152 return false;
154 if(curgain < size(gains))
155 gains[curgain++] = value;
157 std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f);
158 gotgains = true;
160 else if(cmd == "add_row")
162 matrix.emplace_back();
163 AmbDecConf::CoeffArray &mtxrow = matrix.back();
164 std::size_t curidx{0u};
165 float value{};
166 while(istr.good())
168 istr >> value;
169 if(istr.fail()) break;
170 if(!istr.eof() && !std::isspace(istr.peek()))
172 ERR("Extra junk on matrix element %zux%zu: %s\n", curidx,
173 matrix.size(), buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
174 matrix.pop_back();
175 return false;
177 if(curidx < mtxrow.size())
178 mtxrow[curidx++] = value;
180 std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f);
181 cur++;
183 else
185 ERR("Unexpected matrix command: %s\n", cmd.c_str());
186 return false;
189 istr.clear();
190 const auto endpos = static_cast<std::size_t>(istr.tellg());
191 if(!is_at_end(buffer, endpos))
193 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
194 return false;
196 buffer.clear();
199 if(!gotgains)
201 ERR("Matrix order_gain not specified\n");
202 return false;
205 return true;
208 } // namespace
210 int AmbDecConf::load(const char *fname) noexcept
212 al::ifstream f{fname};
213 if(!f.is_open())
215 ERR("Failed to open: %s\n", fname);
216 return 0;
219 std::size_t num_speakers{0u};
220 std::string buffer;
221 while(read_clipped_line(f, buffer))
223 std::istringstream istr{buffer};
225 std::string command{read_word(istr)};
226 if(command.empty())
228 ERR("Malformed line: %s\n", buffer.c_str());
229 return 0;
232 if(command == "/description")
233 istr >> Description;
234 else if(command == "/version")
236 istr >> Version;
237 if(!istr.eof() && !std::isspace(istr.peek()))
239 ERR("Extra junk after version: %s\n",
240 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
241 return 0;
243 if(Version != 3)
245 ERR("Unsupported version: %u\n", Version);
246 return 0;
249 else if(command == "/dec/chan_mask")
251 istr >> std::hex >> ChanMask >> std::dec;
252 if(!istr.eof() && !std::isspace(istr.peek()))
254 ERR("Extra junk after mask: %s\n",
255 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
256 return 0;
259 else if(command == "/dec/freq_bands")
261 istr >> FreqBands;
262 if(!istr.eof() && !std::isspace(istr.peek()))
264 ERR("Extra junk after freq_bands: %s\n",
265 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
266 return 0;
268 if(FreqBands != 1 && FreqBands != 2)
270 ERR("Invalid freq_bands value: %u\n", FreqBands);
271 return 0;
274 else if(command == "/dec/speakers")
276 istr >> num_speakers;
277 if(!istr.eof() && !std::isspace(istr.peek()))
279 ERR("Extra junk after speakers: %s\n",
280 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
281 return 0;
283 Speakers.reserve(num_speakers);
284 LFMatrix.reserve(num_speakers);
285 HFMatrix.reserve(num_speakers);
287 else if(command == "/dec/coeff_scale")
289 std::string scale = read_word(istr);
290 if(scale == "n3d") CoeffScale = AmbDecScale::N3D;
291 else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
292 else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
293 else
295 ERR("Unsupported coeff scale: %s\n", scale.c_str());
296 return 0;
299 else if(command == "/opt/xover_freq")
301 istr >> XOverFreq;
302 if(!istr.eof() && !std::isspace(istr.peek()))
304 ERR("Extra junk after xover_freq: %s\n",
305 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
306 return 0;
309 else if(command == "/opt/xover_ratio")
311 istr >> XOverRatio;
312 if(!istr.eof() && !std::isspace(istr.peek()))
314 ERR("Extra junk after xover_ratio: %s\n",
315 buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
316 return 0;
319 else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" ||
320 command == "/opt/delay_comp" || command == "/opt/level_comp")
322 /* Unused */
323 read_word(istr);
325 else if(command == "/speakers/{")
327 const auto endpos = static_cast<std::size_t>(istr.tellg());
328 if(!is_at_end(buffer, endpos))
330 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
331 return 0;
333 buffer.clear();
335 if(!load_ambdec_speakers(Speakers, num_speakers, f, buffer))
336 return 0;
338 if(!read_clipped_line(f, buffer))
340 ERR("Unexpected end of file\n");
341 return 0;
343 std::istringstream istr2{buffer};
344 std::string endmark{read_word(istr2)};
345 if(endmark != "/}")
347 ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str());
348 return 0;
350 istr.swap(istr2);
352 else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
354 const auto endpos = static_cast<std::size_t>(istr.tellg());
355 if(!is_at_end(buffer, endpos))
357 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
358 return 0;
360 buffer.clear();
362 if(FreqBands == 1)
364 if(command != "/matrix/{")
366 ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str());
367 return 0;
369 if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer))
370 return 0;
372 else
374 if(command == "/lfmatrix/{")
376 if(!load_ambdec_matrix(LFOrderGain, LFMatrix, num_speakers, f, buffer))
377 return 0;
379 else if(command == "/hfmatrix/{")
381 if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer))
382 return 0;
384 else
386 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str());
387 return 0;
391 if(!read_clipped_line(f, buffer))
393 ERR("Unexpected end of file\n");
394 return 0;
396 std::istringstream istr2{buffer};
397 std::string endmark{read_word(istr2)};
398 if(endmark != "/}")
400 ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str());
401 return 0;
403 istr.swap(istr2);
405 else if(command == "/end")
407 const auto endpos = static_cast<std::size_t>(istr.tellg());
408 if(!is_at_end(buffer, endpos))
410 ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos);
411 return 0;
414 return 1;
416 else
418 ERR("Unexpected command: %s\n", command.c_str());
419 return 0;
422 istr.clear();
423 const auto endpos = static_cast<std::size_t>(istr.tellg());
424 if(!is_at_end(buffer, endpos))
426 ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
427 return 0;
429 buffer.clear();
431 ERR("Unexpected end of file\n");
433 return 0;