16 #include "alfstream.h"
18 #include "opthelpers.h"
23 std::string
read_word(std::istream
&f
)
30 bool is_at_end(const std::string
&buffer
, std::size_t endpos
)
32 while(endpos
< buffer
.length() && std::isspace(buffer
[endpos
]))
34 return !(endpos
< buffer
.length() && buffer
[endpos
] != '#');
38 enum class ReaderScope
{
45 #ifdef __USE_MINGW_ANSI_STDIO
46 [[gnu::format(gnu_printf
,2,3)]]
48 [[gnu::format(printf
,2,3)]]
50 al::optional
<std::string
> make_error(size_t linenum
, const char *fmt
, ...)
52 al::optional
<std::string
> ret
;
53 auto &str
= ret
.emplace();
56 int printed
{std::snprintf(const_cast<char*>(str
.data()), str
.length(), "Line %zu: ", linenum
)};
57 if(printed
< 0) printed
= 0;
58 auto plen
= std::min(static_cast<size_t>(printed
), str
.length());
60 std::va_list args
, args2
;
63 const int msglen
{std::vsnprintf(&str
[plen
], str
.size()-plen
, fmt
, args
)};
64 if(msglen
>= 0 && static_cast<size_t>(msglen
) >= str
.size()-plen
)
66 str
.resize(static_cast<size_t>(msglen
) + plen
+ 1u);
67 std::vsnprintf(&str
[plen
], str
.size()-plen
, fmt
, args2
);
77 AmbDecConf::~AmbDecConf() = default;
80 al::optional
<std::string
> AmbDecConf::load(const char *fname
) noexcept
82 al::ifstream f
{fname
};
84 return std::string("Failed to open file \"")+fname
+"\"";
86 ReaderScope scope
{ReaderScope::Global
};
87 size_t speaker_pos
{0};
88 size_t lfmatrix_pos
{0};
89 size_t hfmatrix_pos
{0};
93 while(f
.good() && std::getline(f
, buffer
))
97 std::istringstream istr
{buffer
};
98 std::string command
{read_word(istr
)};
99 if(command
.empty() || command
[0] == '#')
104 if(scope
== ReaderScope::Global
)
105 return make_error(linenum
, "Unexpected /} in global scope");
106 scope
= ReaderScope::Global
;
110 if(scope
== ReaderScope::Speakers
)
112 if(command
== "add_spkr")
114 if(speaker_pos
== NumSpeakers
)
115 return make_error(linenum
, "Too many speakers specified");
117 AmbDecConf::SpeakerConf
&spkr
= Speakers
[speaker_pos
++];
119 istr
>> spkr
.Distance
;
120 istr
>> spkr
.Azimuth
;
121 istr
>> spkr
.Elevation
;
122 istr
>> spkr
.Connection
;
125 return make_error(linenum
, "Unexpected speakers command: %s", command
.c_str());
127 else if(scope
== ReaderScope::LFMatrix
|| scope
== ReaderScope::HFMatrix
)
129 auto &gains
= (scope
== ReaderScope::LFMatrix
) ? LFOrderGain
: HFOrderGain
;
130 auto *matrix
= (scope
== ReaderScope::LFMatrix
) ? LFMatrix
: HFMatrix
;
131 auto &pos
= (scope
== ReaderScope::LFMatrix
) ? lfmatrix_pos
: hfmatrix_pos
;
133 if(command
== "order_gain")
135 size_t toread
{(ChanMask
> Ambi3OrderMask
) ? 5u : 4u};
136 std::size_t curgain
{0u};
142 if(curgain
< al::size(gains
))
143 gains
[curgain
++] = value
;
146 else if(command
== "add_row")
148 if(pos
== NumSpeakers
)
149 return make_error(linenum
, "Too many matrix rows specified");
151 unsigned int mask
{ChanMask
};
153 AmbDecConf::CoeffArray
&mtxrow
= matrix
[pos
++];
159 auto idx
= static_cast<unsigned>(al::countr_zero(mask
));
160 mask
&= ~(1u << idx
);
163 if(idx
< mtxrow
.size())
168 return make_error(linenum
, "Unexpected matrix command: %s", command
.c_str());
170 // Global scope commands
171 else if(command
== "/description")
173 while(istr
.good() && std::isspace(istr
.peek()))
175 std::getline(istr
, Description
);
176 while(!Description
.empty() && std::isspace(Description
.back()))
177 Description
.pop_back();
179 else if(command
== "/version")
182 return make_error(linenum
, "Duplicate version definition");
185 return make_error(linenum
, "Unsupported version: %d", Version
);
187 else if(command
== "/dec/chan_mask")
190 return make_error(linenum
, "Duplicate chan_mask definition");
191 istr
>> std::hex
>> ChanMask
>> std::dec
;
193 if(!ChanMask
|| ChanMask
> Ambi4OrderMask
)
194 return make_error(linenum
, "Invalid chan_mask: 0x%x", ChanMask
);
195 if(ChanMask
> Ambi3OrderMask
&& CoeffScale
== AmbDecScale::FuMa
)
196 return make_error(linenum
, "FuMa not compatible with over third-order");
198 else if(command
== "/dec/freq_bands")
201 return make_error(linenum
, "Duplicate freq_bands");
203 if(FreqBands
!= 1 && FreqBands
!= 2)
204 return make_error(linenum
, "Invalid freq_bands: %u", FreqBands
);
206 else if(command
== "/dec/speakers")
209 return make_error(linenum
, "Duplicate speakers");
212 return make_error(linenum
, "Invalid speakers: %zu", NumSpeakers
);
213 Speakers
= std::make_unique
<SpeakerConf
[]>(NumSpeakers
);
215 else if(command
== "/dec/coeff_scale")
217 if(CoeffScale
!= AmbDecScale::Unset
)
218 return make_error(linenum
, "Duplicate coeff_scale");
220 std::string scale
{read_word(istr
)};
221 if(scale
== "n3d") CoeffScale
= AmbDecScale::N3D
;
222 else if(scale
== "sn3d") CoeffScale
= AmbDecScale::SN3D
;
223 else if(scale
== "fuma") CoeffScale
= AmbDecScale::FuMa
;
225 return make_error(linenum
, "Unexpected coeff_scale: %s", scale
.c_str());
227 if(ChanMask
> Ambi3OrderMask
&& CoeffScale
== AmbDecScale::FuMa
)
228 return make_error(linenum
, "FuMa not compatible with over third-order");
230 else if(command
== "/opt/xover_freq")
234 else if(command
== "/opt/xover_ratio")
238 else if(command
== "/opt/input_scale" || command
== "/opt/nfeff_comp"
239 || command
== "/opt/delay_comp" || command
== "/opt/level_comp")
244 else if(command
== "/speakers/{")
247 return make_error(linenum
, "Speakers defined without a count");
248 scope
= ReaderScope::Speakers
;
250 else if(command
== "/lfmatrix/{" || command
== "/hfmatrix/{" || command
== "/matrix/{")
253 return make_error(linenum
, "Matrix defined without a speaker count");
255 return make_error(linenum
, "Matrix defined without a channel mask");
259 Matrix
= std::make_unique
<CoeffArray
[]>(NumSpeakers
* FreqBands
);
260 LFMatrix
= Matrix
.get();
261 HFMatrix
= LFMatrix
+ NumSpeakers
*(FreqBands
-1);
266 if(command
!= "/matrix/{")
267 return make_error(linenum
, "Unexpected \"%s\" for a single-band decoder",
269 scope
= ReaderScope::HFMatrix
;
273 if(command
== "/lfmatrix/{")
274 scope
= ReaderScope::LFMatrix
;
275 else if(command
== "/hfmatrix/{")
276 scope
= ReaderScope::HFMatrix
;
278 return make_error(linenum
, "Unexpected \"%s\" for a dual-band decoder",
282 else if(command
== "/end")
284 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
285 if(!is_at_end(buffer
, endpos
))
286 return make_error(linenum
, "Extra junk on end: %s", buffer
.substr(endpos
).c_str());
288 if(speaker_pos
< NumSpeakers
|| hfmatrix_pos
< NumSpeakers
289 || (FreqBands
== 2 && lfmatrix_pos
< NumSpeakers
))
290 return make_error(linenum
, "Incomplete decoder definition");
291 if(CoeffScale
== AmbDecScale::Unset
)
292 return make_error(linenum
, "No coefficient scaling defined");
297 return make_error(linenum
, "Unexpected command: %s", command
.c_str());
300 const auto endpos
= static_cast<std::size_t>(istr
.tellg());
301 if(!is_at_end(buffer
, endpos
))
302 return make_error(linenum
, "Extra junk on line: %s", buffer
.substr(endpos
).c_str());
305 return make_error(linenum
, "Unexpected end of file");