19 #include "opthelpers.h"
24 std::string
read_word(std::istream
&f
)
31 bool is_at_end(const std::string
&buffer
, std::size_t endpos
)
33 while(endpos
< buffer
.length() && std::isspace(buffer
[endpos
]))
35 return !(endpos
< buffer
.length() && buffer
[endpos
] != '#');
39 enum class ReaderScope
{
47 [[gnu::format(__MINGW_PRINTF_FORMAT
,2,3)]]
49 [[gnu::format(printf
,2,3)]]
51 std::optional
<std::string
> make_error(size_t linenum
, const char *fmt
, ...)
53 std::optional
<std::string
> ret
;
54 auto &str
= ret
.emplace();
57 int printed
{std::snprintf(str
.data(), str
.length(), "Line %zu: ", linenum
)};
58 if(printed
< 0) printed
= 0;
59 auto plen
= std::min(static_cast<size_t>(printed
), str
.length());
61 /* NOLINTBEGIN(*-array-to-pointer-decay) */
62 std::va_list args
, args2
;
65 const int msglen
{std::vsnprintf(&str
[plen
], str
.size()-plen
, fmt
, args
)};
66 if(msglen
>= 0 && static_cast<size_t>(msglen
) >= str
.size()-plen
)
68 str
.resize(static_cast<size_t>(msglen
) + plen
+ 1u);
69 std::vsnprintf(&str
[plen
], str
.size()-plen
, fmt
, args2
);
73 /* NOLINTEND(*-array-to-pointer-decay) */
80 AmbDecConf::~AmbDecConf() = default;
83 std::optional
<std::string
> AmbDecConf::load(const char *fname
) noexcept
85 std::ifstream f
{std::filesystem::u8path(fname
)};
87 return std::string("Failed to open file \"")+fname
+"\"";
89 ReaderScope scope
{ReaderScope::Global
};
90 size_t speaker_pos
{0};
91 size_t lfmatrix_pos
{0};
92 size_t hfmatrix_pos
{0};
96 while(f
.good() && std::getline(f
, buffer
))
100 std::istringstream istr
{buffer
};
101 std::string command
{read_word(istr
)};
102 if(command
.empty() || command
[0] == '#')
107 if(scope
== ReaderScope::Global
)
108 return make_error(linenum
, "Unexpected /} in global scope");
109 scope
= ReaderScope::Global
;
113 if(scope
== ReaderScope::Speakers
)
115 if(command
== "add_spkr")
117 if(speaker_pos
== Speakers
.size())
118 return make_error(linenum
, "Too many speakers specified");
120 AmbDecConf::SpeakerConf
&spkr
= Speakers
[speaker_pos
++];
122 istr
>> spkr
.Distance
;
123 istr
>> spkr
.Azimuth
;
124 istr
>> spkr
.Elevation
;
125 istr
>> spkr
.Connection
;
128 return make_error(linenum
, "Unexpected speakers command: %s", command
.c_str());
130 else if(scope
== ReaderScope::LFMatrix
|| scope
== ReaderScope::HFMatrix
)
132 auto &gains
= (scope
== ReaderScope::LFMatrix
) ? LFOrderGain
: HFOrderGain
;
133 auto matrix
= (scope
== ReaderScope::LFMatrix
) ? LFMatrix
: HFMatrix
;
134 auto &pos
= (scope
== ReaderScope::LFMatrix
) ? lfmatrix_pos
: hfmatrix_pos
;
136 if(command
== "order_gain")
138 size_t toread
{(ChanMask
> Ambi3OrderMask
) ? 5u : 4u};
139 std::size_t curgain
{0u};
145 if(curgain
< std::size(gains
))
146 gains
[curgain
++] = value
;
149 else if(command
== "add_row")
151 if(pos
== Speakers
.size())
152 return make_error(linenum
, "Too many matrix rows specified");
154 unsigned int mask
{ChanMask
};
156 AmbDecConf::CoeffArray
&mtxrow
= matrix
[pos
++];
162 auto idx
= static_cast<unsigned>(al::countr_zero(mask
));
163 mask
&= ~(1u << idx
);
166 if(idx
< mtxrow
.size())
171 return make_error(linenum
, "Unexpected matrix command: %s", command
.c_str());
173 // Global scope commands
174 else if(command
== "/description")
176 while(istr
.good() && std::isspace(istr
.peek()))
178 std::getline(istr
, Description
);
179 while(!Description
.empty() && std::isspace(Description
.back()))
180 Description
.pop_back();
182 else if(command
== "/version")
185 return make_error(linenum
, "Duplicate version definition");
188 return make_error(linenum
, "Unsupported version: %d", Version
);
190 else if(command
== "/dec/chan_mask")
193 return make_error(linenum
, "Duplicate chan_mask definition");
194 istr
>> std::hex
>> ChanMask
>> std::dec
;
196 if(!ChanMask
|| ChanMask
> Ambi4OrderMask
)
197 return make_error(linenum
, "Invalid chan_mask: 0x%x", ChanMask
);
198 if(ChanMask
> Ambi3OrderMask
&& CoeffScale
== AmbDecScale::FuMa
)
199 return make_error(linenum
, "FuMa not compatible with over third-order");
201 else if(command
== "/dec/freq_bands")
204 return make_error(linenum
, "Duplicate freq_bands");
206 if(FreqBands
!= 1 && FreqBands
!= 2)
207 return make_error(linenum
, "Invalid freq_bands: %u", FreqBands
);
209 else if(command
== "/dec/speakers")
211 if(!Speakers
.empty())
212 return make_error(linenum
, "Duplicate speakers");
213 size_t numspeakers
{};
216 return make_error(linenum
, "Invalid speakers: %zu", numspeakers
);
217 Speakers
.resize(numspeakers
);
219 else if(command
== "/dec/coeff_scale")
221 if(CoeffScale
!= AmbDecScale::Unset
)
222 return make_error(linenum
, "Duplicate coeff_scale");
224 std::string scale
{read_word(istr
)};
225 if(scale
== "n3d") CoeffScale
= AmbDecScale::N3D
;
226 else if(scale
== "sn3d") CoeffScale
= AmbDecScale::SN3D
;
227 else if(scale
== "fuma") CoeffScale
= AmbDecScale::FuMa
;
229 return make_error(linenum
, "Unexpected coeff_scale: %s", scale
.c_str());
231 if(ChanMask
> Ambi3OrderMask
&& CoeffScale
== AmbDecScale::FuMa
)
232 return make_error(linenum
, "FuMa not compatible with over third-order");
234 else if(command
== "/opt/xover_freq")
238 else if(command
== "/opt/xover_ratio")
242 else if(command
== "/opt/input_scale" || command
== "/opt/nfeff_comp"
243 || command
== "/opt/delay_comp" || command
== "/opt/level_comp")
248 else if(command
== "/speakers/{")
251 return make_error(linenum
, "Speakers defined without a count");
252 scope
= ReaderScope::Speakers
;
254 else if(command
== "/lfmatrix/{" || command
== "/hfmatrix/{" || command
== "/matrix/{")
257 return make_error(linenum
, "Matrix defined without a speaker count");
259 return make_error(linenum
, "Matrix defined without a channel mask");
263 Matrix
.resize(Speakers
.size() * FreqBands
);
264 LFMatrix
= al::span
{Matrix
}.first(Speakers
.size());
265 HFMatrix
= al::span
{Matrix
}.subspan(Speakers
.size()*(FreqBands
-1));
270 if(command
!= "/matrix/{")
271 return make_error(linenum
, "Unexpected \"%s\" for a single-band decoder",
273 scope
= ReaderScope::HFMatrix
;
277 if(command
== "/lfmatrix/{")
278 scope
= ReaderScope::LFMatrix
;
279 else if(command
== "/hfmatrix/{")
280 scope
= ReaderScope::HFMatrix
;
282 return make_error(linenum
, "Unexpected \"%s\" for a dual-band decoder",
286 else if(command
== "/end")
288 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
289 if(!is_at_end(buffer
, endpos
))
290 return make_error(linenum
, "Extra junk on end: %s", buffer
.substr(endpos
).c_str());
292 if(speaker_pos
< Speakers
.size() || hfmatrix_pos
< Speakers
.size()
293 || (FreqBands
== 2 && lfmatrix_pos
< Speakers
.size()))
294 return make_error(linenum
, "Incomplete decoder definition");
295 if(CoeffScale
== AmbDecScale::Unset
)
296 return make_error(linenum
, "No coefficient scaling defined");
301 return make_error(linenum
, "Unexpected command: %s", command
.c_str());
304 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
305 if(!is_at_end(buffer
, endpos
))
306 return make_error(linenum
, "Extra junk on line: %s", buffer
.substr(endpos
).c_str());
309 return make_error(linenum
, "Unexpected end of file");