]> git.tdb.fi Git - ext/openal.git/blob - core/ambdec.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / core / ambdec.cpp
1
2 #include "config.h"
3
4 #include "ambdec.h"
5
6 #include <algorithm>
7 #include <cctype>
8 #include <cstdarg>
9 #include <cstddef>
10 #include <cstdio>
11 #include <iterator>
12 #include <sstream>
13 #include <string>
14
15 #include "albit.h"
16 #include "alfstream.h"
17 #include "alspan.h"
18 #include "opthelpers.h"
19
20
21 namespace {
22
23 std::string read_word(std::istream &f)
24 {
25     std::string ret;
26     f >> ret;
27     return ret;
28 }
29
30 bool is_at_end(const std::string &buffer, std::size_t endpos)
31 {
32     while(endpos < buffer.length() && std::isspace(buffer[endpos]))
33         ++endpos;
34     return !(endpos < buffer.length() && buffer[endpos] != '#');
35 }
36
37
38 enum class ReaderScope {
39     Global,
40     Speakers,
41     LFMatrix,
42     HFMatrix,
43 };
44
45 #ifdef __USE_MINGW_ANSI_STDIO
46 [[gnu::format(gnu_printf,2,3)]]
47 #else
48 [[gnu::format(printf,2,3)]]
49 #endif
50 al::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
51 {
52     al::optional<std::string> ret;
53     auto &str = ret.emplace();
54
55     str.resize(256);
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());
59
60     std::va_list args, args2;
61     va_start(args, fmt);
62     va_copy(args2, args);
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)
65     {
66         str.resize(static_cast<size_t>(msglen) + plen + 1u);
67         std::vsnprintf(&str[plen], str.size()-plen, fmt, args2);
68     }
69     va_end(args2);
70     va_end(args);
71
72     return ret;
73 }
74
75 } // namespace
76
77 AmbDecConf::~AmbDecConf() = default;
78
79
80 al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
81 {
82     al::ifstream f{fname};
83     if(!f.is_open())
84         return std::string("Failed to open file \"")+fname+"\"";
85
86     ReaderScope scope{ReaderScope::Global};
87     size_t speaker_pos{0};
88     size_t lfmatrix_pos{0};
89     size_t hfmatrix_pos{0};
90     size_t linenum{0};
91
92     std::string buffer;
93     while(f.good() && std::getline(f, buffer))
94     {
95         ++linenum;
96
97         std::istringstream istr{buffer};
98         std::string command{read_word(istr)};
99         if(command.empty() || command[0] == '#')
100             continue;
101
102         if(command == "/}")
103         {
104             if(scope == ReaderScope::Global)
105                 return make_error(linenum, "Unexpected /} in global scope");
106             scope = ReaderScope::Global;
107             continue;
108         }
109
110         if(scope == ReaderScope::Speakers)
111         {
112             if(command == "add_spkr")
113             {
114                 if(speaker_pos == NumSpeakers)
115                     return make_error(linenum, "Too many speakers specified");
116
117                 AmbDecConf::SpeakerConf &spkr = Speakers[speaker_pos++];
118                 istr >> spkr.Name;
119                 istr >> spkr.Distance;
120                 istr >> spkr.Azimuth;
121                 istr >> spkr.Elevation;
122                 istr >> spkr.Connection;
123             }
124             else
125                 return make_error(linenum, "Unexpected speakers command: %s", command.c_str());
126         }
127         else if(scope == ReaderScope::LFMatrix || scope == ReaderScope::HFMatrix)
128         {
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;
132
133             if(command == "order_gain")
134             {
135                 size_t toread{(ChanMask > Ambi3OrderMask) ? 5u : 4u};
136                 std::size_t curgain{0u};
137                 float value{};
138                 while(toread)
139                 {
140                     --toread;
141                     istr >> value;
142                     if(curgain < al::size(gains))
143                         gains[curgain++] = value;
144                 }
145             }
146             else if(command == "add_row")
147             {
148                 if(pos == NumSpeakers)
149                     return make_error(linenum, "Too many matrix rows specified");
150
151                 unsigned int mask{ChanMask};
152
153                 AmbDecConf::CoeffArray &mtxrow = matrix[pos++];
154                 mtxrow.fill(0.0f);
155
156                 float value{};
157                 while(mask)
158                 {
159                     auto idx = static_cast<unsigned>(al::countr_zero(mask));
160                     mask &= ~(1u << idx);
161
162                     istr >> value;
163                     if(idx < mtxrow.size())
164                         mtxrow[idx] = value;
165                 }
166             }
167             else
168                 return make_error(linenum, "Unexpected matrix command: %s", command.c_str());
169         }
170         // Global scope commands
171         else if(command == "/description")
172         {
173             while(istr.good() && std::isspace(istr.peek()))
174                 istr.ignore();
175             std::getline(istr, Description);
176             while(!Description.empty() && std::isspace(Description.back()))
177                 Description.pop_back();
178         }
179         else if(command == "/version")
180         {
181             if(Version)
182                 return make_error(linenum, "Duplicate version definition");
183             istr >> Version;
184             if(Version != 3)
185                 return make_error(linenum, "Unsupported version: %d", Version);
186         }
187         else if(command == "/dec/chan_mask")
188         {
189             if(ChanMask)
190                 return make_error(linenum, "Duplicate chan_mask definition");
191             istr >> std::hex >> ChanMask >> std::dec;
192
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");
197         }
198         else if(command == "/dec/freq_bands")
199         {
200             if(FreqBands)
201                 return make_error(linenum, "Duplicate freq_bands");
202             istr >> FreqBands;
203             if(FreqBands != 1 && FreqBands != 2)
204                 return make_error(linenum, "Invalid freq_bands: %u", FreqBands);
205         }
206         else if(command == "/dec/speakers")
207         {
208             if(NumSpeakers)
209                 return make_error(linenum, "Duplicate speakers");
210             istr >> NumSpeakers;
211             if(!NumSpeakers)
212                 return make_error(linenum, "Invalid speakers: %zu", NumSpeakers);
213             Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers);
214         }
215         else if(command == "/dec/coeff_scale")
216         {
217             if(CoeffScale != AmbDecScale::Unset)
218                 return make_error(linenum, "Duplicate coeff_scale");
219
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;
224             else
225                 return make_error(linenum, "Unexpected coeff_scale: %s", scale.c_str());
226
227             if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa)
228                 return make_error(linenum, "FuMa not compatible with over third-order");
229         }
230         else if(command == "/opt/xover_freq")
231         {
232             istr >> XOverFreq;
233         }
234         else if(command == "/opt/xover_ratio")
235         {
236             istr >> XOverRatio;
237         }
238         else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp"
239             || command == "/opt/delay_comp" || command == "/opt/level_comp")
240         {
241             /* Unused */
242             read_word(istr);
243         }
244         else if(command == "/speakers/{")
245         {
246             if(!NumSpeakers)
247                 return make_error(linenum, "Speakers defined without a count");
248             scope = ReaderScope::Speakers;
249         }
250         else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
251         {
252             if(!NumSpeakers)
253                 return make_error(linenum, "Matrix defined without a speaker count");
254             if(!ChanMask)
255                 return make_error(linenum, "Matrix defined without a channel mask");
256
257             if(!Matrix)
258             {
259                 Matrix = std::make_unique<CoeffArray[]>(NumSpeakers * FreqBands);
260                 LFMatrix = Matrix.get();
261                 HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1);
262             }
263
264             if(FreqBands == 1)
265             {
266                 if(command != "/matrix/{")
267                     return make_error(linenum, "Unexpected \"%s\" for a single-band decoder",
268                         command.c_str());
269                 scope = ReaderScope::HFMatrix;
270             }
271             else
272             {
273                 if(command == "/lfmatrix/{")
274                     scope = ReaderScope::LFMatrix;
275                 else if(command == "/hfmatrix/{")
276                     scope = ReaderScope::HFMatrix;
277                 else
278                     return make_error(linenum, "Unexpected \"%s\" for a dual-band decoder",
279                         command.c_str());
280             }
281         }
282         else if(command == "/end")
283         {
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());
287
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");
293
294             return al::nullopt;
295         }
296         else
297             return make_error(linenum, "Unexpected command: %s", command.c_str());
298
299         istr.clear();
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());
303         buffer.clear();
304     }
305     return make_error(linenum, "Unexpected end of file");
306 }