13 #include "alfstream.h"
14 #include "core/logging.h"
19 template<typename T
, std::size_t N
>
20 constexpr inline std::size_t size(const T(&)[N
]) noexcept
23 int readline(std::istream
&f
, std::string
&output
)
25 while(f
.good() && f
.peek() == '\n')
28 return std::getline(f
, output
) && !output
.empty();
31 bool read_clipped_line(std::istream
&f
, std::string
&buffer
)
33 while(readline(f
, buffer
))
36 while(pos
< buffer
.length() && std::isspace(buffer
[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()))
53 std::string
read_word(std::istream
&f
)
60 bool is_at_end(const std::string
&buffer
, std::size_t endpos
)
62 while(endpos
< buffer
.length() && std::isspace(buffer
[endpos
]))
64 return !(endpos
< buffer
.length());
68 al::optional
<std::string
> load_ambdec_speakers(AmbDecConf::SpeakerConf
*spkrs
,
69 const std::size_t num_speakers
, std::istream
&f
, std::string
&buffer
)
71 size_t cur_speaker
{0};
72 while(cur_speaker
< num_speakers
)
74 std::istringstream istr
{buffer
};
76 std::string cmd
{read_word(istr
)};
79 if(!read_clipped_line(f
, buffer
))
80 return al::make_optional
<std::string
>("Unexpected end of file");
86 AmbDecConf::SpeakerConf
&spkr
= spkrs
[cur_speaker
++];
87 const size_t spkr_num
{cur_speaker
};
90 if(istr
.fail()) WARN("Name not specified for speaker %zu\n", spkr_num
);
91 istr
>> spkr
.Distance
;
92 if(istr
.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num
);
94 if(istr
.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num
);
95 istr
>> spkr
.Elevation
;
96 if(istr
.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num
);
97 istr
>> spkr
.Connection
;
98 if(istr
.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num
);
101 return al::make_optional("Unexpected speakers command: "+cmd
);
104 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
105 if(!is_at_end(buffer
, endpos
))
106 return al::make_optional("Extra junk on line: " + buffer
.substr(endpos
));
113 al::optional
<std::string
> load_ambdec_matrix(float (&gains
)[MaxAmbiOrder
+1],
114 AmbDecConf::CoeffArray
*matrix
, const std::size_t maxrow
, std::istream
&f
, std::string
&buffer
)
116 bool gotgains
{false};
120 std::istringstream istr
{buffer
};
122 std::string cmd
{read_word(istr
)};
125 if(!read_clipped_line(f
, buffer
))
126 return al::make_optional
<std::string
>("Unexpected end of file");
130 if(cmd
== "order_gain")
132 std::size_t curgain
{0u};
137 if(istr
.fail()) break;
138 if(!istr
.eof() && !std::isspace(istr
.peek()))
139 return al::make_optional("Extra junk on gain "+std::to_string(curgain
+1)+": "+
140 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
141 if(curgain
< size(gains
))
142 gains
[curgain
++] = value
;
144 std::fill(std::begin(gains
)+curgain
, std::end(gains
), 0.0f
);
147 else if(cmd
== "add_row")
149 AmbDecConf::CoeffArray
&mtxrow
= matrix
[cur
++];
150 std::size_t curidx
{0u};
155 if(istr
.fail()) break;
156 if(!istr
.eof() && !std::isspace(istr
.peek()))
157 return al::make_optional("Extra junk on matrix element "+
158 std::to_string(curidx
)+"x"+std::to_string(cur
-1)+": "+
159 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
160 if(curidx
< mtxrow
.size())
161 mtxrow
[curidx
++] = value
;
163 std::fill(mtxrow
.begin()+curidx
, mtxrow
.end(), 0.0f
);
166 return al::make_optional("Unexpected matrix command: "+cmd
);
169 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
170 if(!is_at_end(buffer
, endpos
))
171 return al::make_optional("Extra junk on line: " + buffer
.substr(endpos
));
176 return al::make_optional
<std::string
>("Matrix order_gain not specified");
182 AmbDecConf::~AmbDecConf() = default;
185 al::optional
<std::string
> AmbDecConf::load(const char *fname
) noexcept
187 al::ifstream f
{fname
};
189 return al::make_optional
<std::string
>("Failed to open file");
191 bool speakers_loaded
{false};
192 bool matrix_loaded
{false};
193 bool lfmatrix_loaded
{false};
195 while(read_clipped_line(f
, buffer
))
197 std::istringstream istr
{buffer
};
199 std::string command
{read_word(istr
)};
201 return al::make_optional("Malformed line: "+buffer
);
203 if(command
== "/description")
204 readline(istr
, Description
);
205 else if(command
== "/version")
208 if(!istr
.eof() && !std::isspace(istr
.peek()))
209 return al::make_optional("Extra junk after version: " +
210 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
212 return al::make_optional("Unsupported version: "+std::to_string(Version
));
214 else if(command
== "/dec/chan_mask")
217 return al::make_optional
<std::string
>("Duplicate chan_mask definition");
219 istr
>> std::hex
>> ChanMask
>> std::dec
;
220 if(!istr
.eof() && !std::isspace(istr
.peek()))
221 return al::make_optional("Extra junk after mask: " +
222 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
225 return al::make_optional("Invalid chan_mask: "+std::to_string(ChanMask
));
227 else if(command
== "/dec/freq_bands")
230 return al::make_optional
<std::string
>("Duplicate freq_bands");
233 if(!istr
.eof() && !std::isspace(istr
.peek()))
234 return al::make_optional("Extra junk after freq_bands: " +
235 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
237 if(FreqBands
!= 1 && FreqBands
!= 2)
238 return al::make_optional("Invalid freq_bands: "+std::to_string(FreqBands
));
240 else if(command
== "/dec/speakers")
243 return al::make_optional
<std::string
>("Duplicate speakers");
246 if(!istr
.eof() && !std::isspace(istr
.peek()))
247 return al::make_optional("Extra junk after speakers: " +
248 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
251 return al::make_optional("Invalid speakers: "+std::to_string(NumSpeakers
));
252 Speakers
= std::make_unique
<SpeakerConf
[]>(NumSpeakers
);
254 else if(command
== "/dec/coeff_scale")
256 std::string scale
= read_word(istr
);
257 if(scale
== "n3d") CoeffScale
= AmbDecScale::N3D
;
258 else if(scale
== "sn3d") CoeffScale
= AmbDecScale::SN3D
;
259 else if(scale
== "fuma") CoeffScale
= AmbDecScale::FuMa
;
261 return al::make_optional("Unexpected coeff_scale: "+scale
);
263 else if(command
== "/opt/xover_freq")
266 if(!istr
.eof() && !std::isspace(istr
.peek()))
267 return al::make_optional("Extra junk after xover_freq: " +
268 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
270 else if(command
== "/opt/xover_ratio")
273 if(!istr
.eof() && !std::isspace(istr
.peek()))
274 return al::make_optional("Extra junk after xover_ratio: " +
275 buffer
.substr(static_cast<std::size_t>(istr
.tellg())));
277 else if(command
== "/opt/input_scale" || command
== "/opt/nfeff_comp" ||
278 command
== "/opt/delay_comp" || command
== "/opt/level_comp")
283 else if(command
== "/speakers/{")
286 return al::make_optional
<std::string
>("Speakers defined without a count");
288 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
289 if(!is_at_end(buffer
, endpos
))
290 return al::make_optional("Extra junk on line: " + buffer
.substr(endpos
));
293 if(auto err
= load_ambdec_speakers(Speakers
.get(), NumSpeakers
, f
, buffer
))
295 speakers_loaded
= true;
297 if(!read_clipped_line(f
, buffer
))
298 return al::make_optional
<std::string
>("Unexpected end of file");
299 std::istringstream istr2
{buffer
};
300 std::string endmark
{read_word(istr2
)};
302 return al::make_optional("Expected /} after speaker definitions, got "+endmark
);
305 else if(command
== "/lfmatrix/{" || command
== "/hfmatrix/{" || command
== "/matrix/{")
308 return al::make_optional
<std::string
>("Matrix defined without a count");
309 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
310 if(!is_at_end(buffer
, endpos
))
311 return al::make_optional("Extra junk on line: " + buffer
.substr(endpos
));
316 Matrix
= std::make_unique
<CoeffArray
[]>(NumSpeakers
* FreqBands
);
317 LFMatrix
= Matrix
.get();
318 HFMatrix
= LFMatrix
+ NumSpeakers
*(FreqBands
-1);
323 if(command
!= "/matrix/{")
324 return al::make_optional(
325 "Unexpected \""+command
+"\" type for a single-band decoder");
326 if(auto err
= load_ambdec_matrix(HFOrderGain
, HFMatrix
, NumSpeakers
, f
, buffer
))
328 matrix_loaded
= true;
332 if(command
== "/lfmatrix/{")
334 if(auto err
=load_ambdec_matrix(LFOrderGain
, LFMatrix
, NumSpeakers
, f
, buffer
))
336 lfmatrix_loaded
= true;
338 else if(command
== "/hfmatrix/{")
340 if(auto err
=load_ambdec_matrix(HFOrderGain
, HFMatrix
, NumSpeakers
, f
, buffer
))
342 matrix_loaded
= true;
345 return al::make_optional(
346 "Unexpected \""+command
+"\" type for a dual-band decoder");
349 if(!read_clipped_line(f
, buffer
))
350 return al::make_optional
<std::string
>("Unexpected end of file");
351 std::istringstream istr2
{buffer
};
352 std::string endmark
{read_word(istr2
)};
354 return al::make_optional("Expected /} after matrix definitions, got "+endmark
);
357 else if(command
== "/end")
359 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
360 if(!is_at_end(buffer
, endpos
))
361 return al::make_optional("Extra junk on end: " + buffer
.substr(endpos
));
363 if(!speakers_loaded
|| !matrix_loaded
|| (FreqBands
== 2 && !lfmatrix_loaded
))
364 return al::make_optional
<std::string
>("No decoder defined");
369 return al::make_optional("Unexpected command: " + command
);
372 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
373 if(!is_at_end(buffer
, endpos
))
374 return al::make_optional("Extra junk on line: " + buffer
.substr(endpos
));
377 return al::make_optional
<std::string
>("Unexpected end of file");