Fixed compilation as a subdirectory with MinGW (#991)
[openal-soft.git] / utils / makemhr / loaddef.cpp
blob7685980d7cf3d42b3354998fd1800601ef7854cf
1 /*
2 * HRTF utility for producing and demonstrating the process of creating an
3 * OpenAL Soft compatible HRIR data set.
5 * Copyright (C) 2011-2019 Christopher Fitzgerald
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24 #include "loaddef.h"
26 #include <algorithm>
27 #include <cctype>
28 #include <cmath>
29 #include <cstdarg>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 #include <filesystem>
34 #include <fstream>
35 #include <iterator>
36 #include <limits>
37 #include <memory>
38 #include <optional>
39 #include <string>
40 #include <string_view>
41 #include <vector>
43 #include "albit.h"
44 #include "almalloc.h"
45 #include "alnumeric.h"
46 #include "alspan.h"
47 #include "alstring.h"
48 #include "makemhr.h"
49 #include "polyphase_resampler.h"
51 #include "mysofa.h"
53 namespace {
55 // Constants for accessing the token reader's ring buffer.
56 constexpr uint TRRingBits{16};
57 constexpr uint TRRingSize{1 << TRRingBits};
58 constexpr uint TRRingMask{TRRingSize - 1};
60 // The token reader's load interval in bytes.
61 constexpr uint TRLoadSize{TRRingSize >> 2};
63 // Token reader state for parsing the data set definition.
64 struct TokenReaderT {
65 std::istream &mIStream;
66 std::string mName{};
67 uint mLine{};
68 uint mColumn{};
69 std::array<char,TRRingSize> mRing{};
70 std::streamsize mIn{};
71 std::streamsize mOut{};
73 TokenReaderT(std::istream &istream) noexcept : mIStream{istream} { }
74 TokenReaderT(const TokenReaderT&) = default;
78 // The maximum identifier length used when processing the data set
79 // definition.
80 constexpr uint MaxIdentLen{16};
82 // The limits for the listener's head 'radius' in the data set definition.
83 constexpr double MinRadius{0.05};
84 constexpr double MaxRadius{0.15};
86 // The maximum number of channels that can be addressed for a WAVE file
87 // source listed in the data set definition.
88 constexpr uint MaxWaveChannels{65535};
90 // The limits to the byte size for a binary source listed in the definition
91 // file.
92 enum : uint {
93 MinBinSize = 2,
94 MaxBinSize = 4
97 // The limits to the number of significant bits for an ASCII source listed in
98 // the data set definition.
99 enum : uint {
100 MinASCIIBits = 16,
101 MaxASCIIBits = 32
104 // The four-character-codes for RIFF/RIFX WAVE file chunks.
105 enum : uint {
106 FOURCC_RIFF = 0x46464952, // 'RIFF'
107 FOURCC_RIFX = 0x58464952, // 'RIFX'
108 FOURCC_WAVE = 0x45564157, // 'WAVE'
109 FOURCC_FMT = 0x20746D66, // 'fmt '
110 FOURCC_DATA = 0x61746164, // 'data'
111 FOURCC_LIST = 0x5453494C, // 'LIST'
112 FOURCC_WAVL = 0x6C766177, // 'wavl'
113 FOURCC_SLNT = 0x746E6C73, // 'slnt'
116 // The supported wave formats.
117 enum : uint {
118 WAVE_FORMAT_PCM = 0x0001,
119 WAVE_FORMAT_IEEE_FLOAT = 0x0003,
120 WAVE_FORMAT_EXTENSIBLE = 0xFFFE,
124 enum ByteOrderT {
125 BO_NONE,
126 BO_LITTLE,
127 BO_BIG
130 // Source format for the references listed in the data set definition.
131 enum SourceFormatT {
132 SF_NONE,
133 SF_ASCII, // ASCII text file.
134 SF_BIN_LE, // Little-endian binary file.
135 SF_BIN_BE, // Big-endian binary file.
136 SF_WAVE, // RIFF/RIFX WAVE file.
137 SF_SOFA // Spatially Oriented Format for Accoustics (SOFA) file.
140 // Element types for the references listed in the data set definition.
141 enum ElementTypeT {
142 ET_NONE,
143 ET_INT, // Integer elements.
144 ET_FP // Floating-point elements.
147 // Source reference state used when loading sources.
148 struct SourceRefT {
149 SourceFormatT mFormat;
150 ElementTypeT mType;
151 uint mSize;
152 int mBits;
153 uint mChannel;
154 double mAzimuth;
155 double mElevation;
156 double mRadius;
157 uint mSkip;
158 uint mOffset;
159 std::array<char,MAX_PATH_LEN+1> mPath;
163 /* Whitespace is not significant. It can process tokens as identifiers, numbers
164 * (integer and floating-point), strings, and operators. Strings must be
165 * encapsulated by double-quotes and cannot span multiple lines.
168 // Setup the reader on the given file. The filename can be NULL if no error
169 // output is desired.
170 void TrSetup(const al::span<const char> startbytes, const std::string_view filename,
171 TokenReaderT *tr)
173 std::string_view namepart;
175 if(!filename.empty())
177 const auto fslashpos = filename.rfind('/');
178 const auto bslashpos = filename.rfind('\\');
179 const auto slashpos = (bslashpos >= filename.size()) ? fslashpos :
180 (fslashpos >= filename.size()) ? bslashpos :
181 std::max(fslashpos, bslashpos);
182 if(slashpos < filename.size())
183 namepart = filename.substr(slashpos+1);
186 tr->mName = namepart;
187 tr->mLine = 1;
188 tr->mColumn = 1;
189 tr->mIn = 0;
190 tr->mOut = 0;
192 if(!startbytes.empty())
194 assert(startbytes.size() <= tr->mRing.size());
195 std::copy(startbytes.cbegin(), startbytes.cend(), tr->mRing.begin());
196 tr->mIn += std::streamsize(startbytes.size());
200 // Prime the reader's ring buffer, and return a result indicating that there
201 // is text to process.
202 auto TrLoad(TokenReaderT *tr) -> int
204 std::istream &istream = tr->mIStream;
206 std::streamsize toLoad{TRRingSize - static_cast<std::streamsize>(tr->mIn - tr->mOut)};
207 if(toLoad >= TRLoadSize && istream.good())
209 // Load TRLoadSize (or less if at the end of the file) per read.
210 toLoad = TRLoadSize;
212 const auto in = tr->mIn&TRRingMask;
213 std::streamsize count{TRRingSize - in};
214 if(count < toLoad)
216 istream.read(al::to_address(tr->mRing.begin() + in), count);
217 tr->mIn += istream.gcount();
218 istream.read(tr->mRing.data(), toLoad-count);
219 tr->mIn += istream.gcount();
221 else
223 istream.read(al::to_address(tr->mRing.begin() + in), toLoad);
224 tr->mIn += istream.gcount();
227 if(tr->mOut >= TRRingSize)
229 tr->mOut -= TRRingSize;
230 tr->mIn -= TRRingSize;
233 if(tr->mIn > tr->mOut)
234 return 1;
235 return 0;
238 // Error display routine. Only displays when the base name is not NULL.
239 void TrErrorVA(const TokenReaderT *tr, uint line, uint column, const char *format, va_list argPtr)
241 if(tr->mName.empty())
242 return;
243 fprintf(stderr, "\nError (%s:%u:%u): ", tr->mName.c_str(), line, column);
244 vfprintf(stderr, format, argPtr);
247 // Used to display an error at a saved line/column.
248 void TrErrorAt(const TokenReaderT *tr, uint line, uint column, const char *format, ...)
250 /* NOLINTBEGIN(*-array-to-pointer-decay) */
251 va_list argPtr;
252 va_start(argPtr, format);
253 TrErrorVA(tr, line, column, format, argPtr);
254 va_end(argPtr);
255 /* NOLINTEND(*-array-to-pointer-decay) */
258 // Used to display an error at the current line/column.
259 void TrError(const TokenReaderT *tr, const char *format, ...)
261 /* NOLINTBEGIN(*-array-to-pointer-decay) */
262 va_list argPtr;
263 va_start(argPtr, format);
264 TrErrorVA(tr, tr->mLine, tr->mColumn, format, argPtr);
265 va_end(argPtr);
266 /* NOLINTEND(*-array-to-pointer-decay) */
269 // Skips to the next line.
270 void TrSkipLine(TokenReaderT *tr)
272 char ch;
274 while(TrLoad(tr))
276 ch = tr->mRing[tr->mOut&TRRingMask];
277 tr->mOut++;
278 if(ch == '\n')
280 tr->mLine++;
281 tr->mColumn = 1;
282 break;
284 tr->mColumn ++;
288 // Skips to the next token.
289 auto TrSkipWhitespace(TokenReaderT *tr) -> int
291 while(TrLoad(tr))
293 char ch{tr->mRing[tr->mOut&TRRingMask]};
294 if(isspace(ch))
296 tr->mOut++;
297 if(ch == '\n')
299 tr->mLine++;
300 tr->mColumn = 1;
302 else
303 tr->mColumn++;
305 else if(ch == '#')
306 TrSkipLine(tr);
307 else
308 return 1;
310 return 0;
313 // Get the line and/or column of the next token (or the end of input).
314 void TrIndication(TokenReaderT *tr, uint *line, uint *column)
316 TrSkipWhitespace(tr);
317 if(line) *line = tr->mLine;
318 if(column) *column = tr->mColumn;
321 // Checks to see if a token is (likely to be) an identifier. It does not
322 // display any errors and will not proceed to the next token.
323 auto TrIsIdent(TokenReaderT *tr) -> int
325 if(!TrSkipWhitespace(tr))
326 return 0;
327 char ch{tr->mRing[tr->mOut&TRRingMask]};
328 return ch == '_' || isalpha(ch);
332 // Checks to see if a token is the given operator. It does not display any
333 // errors and will not proceed to the next token.
334 auto TrIsOperator(TokenReaderT *tr, const std::string_view op) -> int
336 if(!TrSkipWhitespace(tr))
337 return 0;
338 auto out = tr->mOut;
339 size_t len{0};
340 while(len < op.size() && out < tr->mIn)
342 if(tr->mRing[out&TRRingMask] != op[len])
343 break;
344 ++len;
345 ++out;
347 if(len == op.size())
348 return 1;
349 return 0;
352 /* The TrRead*() routines obtain the value of a matching token type. They
353 * display type, form, and boundary errors and will proceed to the next
354 * token.
357 // Reads and validates an identifier token.
358 auto TrReadIdent(TokenReaderT *tr, const al::span<char> ident) -> int
360 assert(!ident.empty());
361 const size_t maxLen{ident.size()-1};
362 uint col{tr->mColumn};
363 if(TrSkipWhitespace(tr))
365 col = tr->mColumn;
366 char ch{tr->mRing[tr->mOut&TRRingMask]};
367 if(ch == '_' || isalpha(ch))
369 size_t len{0};
370 do {
371 if(len < maxLen)
372 ident[len] = ch;
373 ++len;
374 tr->mOut++;
375 if(!TrLoad(tr))
376 break;
377 ch = tr->mRing[tr->mOut&TRRingMask];
378 } while(ch == '_' || isdigit(ch) || isalpha(ch));
380 tr->mColumn += static_cast<uint>(len);
381 if(len < maxLen)
383 ident[len] = '\0';
384 return 1;
386 TrErrorAt(tr, tr->mLine, col, "Identifier is too long.\n");
387 return 0;
390 TrErrorAt(tr, tr->mLine, col, "Expected an identifier.\n");
391 return 0;
394 // Reads and validates (including bounds) an integer token.
395 auto TrReadInt(TokenReaderT *tr, const int loBound, const int hiBound, int *value) -> int
397 uint col{tr->mColumn};
398 if(TrSkipWhitespace(tr))
400 col = tr->mColumn;
401 uint len{0};
402 std::array<char,64+1> temp{};
403 char ch{tr->mRing[tr->mOut&TRRingMask]};
404 if(ch == '+' || ch == '-')
406 temp[len] = ch;
407 len++;
408 tr->mOut++;
410 uint digis{0};
411 while(TrLoad(tr))
413 ch = tr->mRing[tr->mOut&TRRingMask];
414 if(!isdigit(ch)) break;
415 if(len < 64)
416 temp[len] = ch;
417 len++;
418 digis++;
419 tr->mOut++;
421 tr->mColumn += len;
422 if(digis > 0 && ch != '.' && !isalpha(ch))
424 if(len > 64)
426 TrErrorAt(tr, tr->mLine, col, "Integer is too long.");
427 return 0;
429 temp[len] = '\0';
430 *value = static_cast<int>(strtol(temp.data(), nullptr, 10));
431 if(*value < loBound || *value > hiBound)
433 TrErrorAt(tr, tr->mLine, col, "Expected a value from %d to %d.\n", loBound, hiBound);
434 return 0;
436 return 1;
439 TrErrorAt(tr, tr->mLine, col, "Expected an integer.\n");
440 return 0;
443 // Reads and validates (including bounds) a float token.
444 auto TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBound, double *value) -> int
446 uint col{tr->mColumn};
447 if(TrSkipWhitespace(tr))
449 col = tr->mColumn;
450 std::array<char,64+1> temp{};
451 uint len{0};
452 char ch{tr->mRing[tr->mOut&TRRingMask]};
453 if(ch == '+' || ch == '-')
455 temp[len] = ch;
456 len++;
457 tr->mOut++;
460 uint digis{0};
461 while(TrLoad(tr))
463 ch = tr->mRing[tr->mOut&TRRingMask];
464 if(!isdigit(ch)) break;
465 if(len < 64)
466 temp[len] = ch;
467 len++;
468 digis++;
469 tr->mOut++;
471 if(ch == '.')
473 if(len < 64)
474 temp[len] = ch;
475 len++;
476 tr->mOut++;
478 while(TrLoad(tr))
480 ch = tr->mRing[tr->mOut&TRRingMask];
481 if(!isdigit(ch)) break;
482 if(len < 64)
483 temp[len] = ch;
484 len++;
485 digis++;
486 tr->mOut++;
488 if(digis > 0)
490 if(ch == 'E' || ch == 'e')
492 if(len < 64)
493 temp[len] = ch;
494 len++;
495 digis = 0;
496 tr->mOut++;
497 if(ch == '+' || ch == '-')
499 if(len < 64)
500 temp[len] = ch;
501 len++;
502 tr->mOut++;
504 while(TrLoad(tr))
506 ch = tr->mRing[tr->mOut&TRRingMask];
507 if(!isdigit(ch)) break;
508 if(len < 64)
509 temp[len] = ch;
510 len++;
511 digis++;
512 tr->mOut++;
515 tr->mColumn += len;
516 if(digis > 0 && ch != '.' && !isalpha(ch))
518 if(len > 64)
520 TrErrorAt(tr, tr->mLine, col, "Float is too long.");
521 return 0;
523 temp[len] = '\0';
524 *value = strtod(temp.data(), nullptr);
525 if(*value < loBound || *value > hiBound)
527 TrErrorAt(tr, tr->mLine, col, "Expected a value from %f to %f.\n", loBound, hiBound);
528 return 0;
530 return 1;
533 else
534 tr->mColumn += len;
536 TrErrorAt(tr, tr->mLine, col, "Expected a float.\n");
537 return 0;
540 // Reads and validates a string token.
541 auto TrReadString(TokenReaderT *tr, const al::span<char> text) -> int
543 assert(!text.empty());
544 const size_t maxLen{text.size()-1};
546 uint col{tr->mColumn};
547 if(TrSkipWhitespace(tr))
549 col = tr->mColumn;
550 if(char ch{tr->mRing[tr->mOut&TRRingMask]}; ch == '\"')
552 tr->mOut++;
553 size_t len{0};
554 while(TrLoad(tr))
556 ch = tr->mRing[tr->mOut&TRRingMask];
557 tr->mOut++;
558 if(ch == '\"')
559 break;
560 if(ch == '\n')
562 TrErrorAt(tr, tr->mLine, col, "Unterminated string at end of line.\n");
563 return 0;
565 if(len < maxLen)
566 text[len] = ch;
567 len++;
569 if(ch != '\"')
571 tr->mColumn += static_cast<uint>(1 + len);
572 TrErrorAt(tr, tr->mLine, col, "Unterminated string at end of input.\n");
573 return 0;
575 tr->mColumn += static_cast<uint>(2 + len);
576 if(len > maxLen)
578 TrErrorAt(tr, tr->mLine, col, "String is too long.\n");
579 return 0;
581 text[len] = '\0';
582 return 1;
585 TrErrorAt(tr, tr->mLine, col, "Expected a string.\n");
586 return 0;
589 // Reads and validates the given operator.
590 auto TrReadOperator(TokenReaderT *tr, const std::string_view op) -> int
592 uint col{tr->mColumn};
593 if(TrSkipWhitespace(tr))
595 col = tr->mColumn;
596 size_t len{0};
597 while(len < op.size() && TrLoad(tr))
599 if(tr->mRing[tr->mOut&TRRingMask] != op[len])
600 break;
601 ++len;
602 tr->mOut += 1;
604 tr->mColumn += static_cast<uint>(len);
605 if(len == op.size())
606 return 1;
608 TrErrorAt(tr, tr->mLine, col, "Expected '%s' operator.\n", op);
609 return 0;
613 /*************************
614 *** File source input ***
615 *************************/
617 // Read a binary value of the specified byte order and byte size from a file,
618 // storing it as a 32-bit unsigned integer.
619 auto ReadBin4(std::istream &istream, const char *filename, const ByteOrderT order,
620 const uint bytes, uint32_t *out) -> int
622 std::array<uint8_t,4> in{};
623 istream.read(reinterpret_cast<char*>(in.data()), static_cast<int>(bytes));
624 if(istream.gcount() != bytes)
626 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
627 return 0;
629 uint32_t accum{0};
630 switch(order)
632 case BO_LITTLE:
633 for(uint i = 0;i < bytes;i++)
634 accum = (accum<<8) | in[bytes - i - 1];
635 break;
636 case BO_BIG:
637 for(uint i = 0;i < bytes;i++)
638 accum = (accum<<8) | in[i];
639 break;
640 default:
641 break;
643 *out = accum;
644 return 1;
647 // Read a binary value of the specified byte order from a file, storing it as
648 // a 64-bit unsigned integer.
649 auto ReadBin8(std::istream &istream, const char *filename, const ByteOrderT order, uint64_t *out) -> int
651 std::array<uint8_t,8> in{};
652 istream.read(reinterpret_cast<char*>(in.data()), 8);
653 if(istream.gcount() != 8)
655 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
656 return 0;
659 uint64_t accum{};
660 switch(order)
662 case BO_LITTLE:
663 for(uint i{0};i < 8;++i)
664 accum = (accum<<8) | in[8 - i - 1];
665 break;
666 case BO_BIG:
667 for(uint i{0};i < 8;++i)
668 accum = (accum<<8) | in[i];
669 break;
670 default:
671 break;
673 *out = accum;
674 return 1;
677 /* Read a binary value of the specified type, byte order, and byte size from
678 * a file, converting it to a double. For integer types, the significant
679 * bits are used to normalize the result. The sign of bits determines
680 * whether they are padded toward the MSB (negative) or LSB (positive).
681 * Floating-point types are not normalized.
683 auto ReadBinAsDouble(std::istream &istream, const char *filename, const ByteOrderT order,
684 const ElementTypeT type, const uint bytes, const int bits, double *out) -> int
686 *out = 0.0;
687 if(bytes > 4)
689 uint64_t val{};
690 if(!ReadBin8(istream, filename, order, &val))
691 return 0;
692 if(type == ET_FP)
693 *out = al::bit_cast<double>(val);
695 else
697 uint32_t val{};
698 if(!ReadBin4(istream, filename, order, bytes, &val))
699 return 0;
700 if(type == ET_FP)
701 *out = al::bit_cast<float>(val);
702 else
704 if(bits > 0)
705 val >>= (8*bytes) - (static_cast<uint>(bits));
706 else
707 val &= (0xFFFFFFFF >> (32+bits));
709 if(val&static_cast<uint>(1<<(std::abs(bits)-1)))
710 val |= (0xFFFFFFFF << std::abs(bits));
711 *out = static_cast<int32_t>(val) / static_cast<double>(1<<(std::abs(bits)-1));
714 return 1;
717 /* Read an ascii value of the specified type from a file, converting it to a
718 * double. For integer types, the significant bits are used to normalize the
719 * result. The sign of the bits should always be positive. This also skips
720 * up to one separator character before the element itself.
722 auto ReadAsciiAsDouble(TokenReaderT *tr, const char *filename, const ElementTypeT type,
723 const uint bits, double *out) -> int
725 if(TrIsOperator(tr, ","))
726 TrReadOperator(tr, ",");
727 else if(TrIsOperator(tr, ":"))
728 TrReadOperator(tr, ":");
729 else if(TrIsOperator(tr, ";"))
730 TrReadOperator(tr, ";");
731 else if(TrIsOperator(tr, "|"))
732 TrReadOperator(tr, "|");
734 if(type == ET_FP)
736 if(!TrReadFloat(tr, -std::numeric_limits<double>::infinity(),
737 std::numeric_limits<double>::infinity(), out))
739 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
740 return 0;
743 else
745 int v;
746 if(!TrReadInt(tr, -(1<<(bits-1)), (1<<(bits-1))-1, &v))
748 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
749 return 0;
751 *out = v / static_cast<double>((1<<(bits-1))-1);
753 return 1;
756 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
757 // the source parameters and data set metrics.
758 auto ReadWaveFormat(std::istream &istream, const ByteOrderT order, const uint hrirRate,
759 SourceRefT *src) -> int
761 uint32_t fourCC, chunkSize;
762 uint32_t format, channels, rate, dummy, block, size, bits;
764 chunkSize = 0;
765 do {
766 if(chunkSize > 0)
767 istream.seekg(static_cast<int>(chunkSize), std::ios::cur);
768 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
769 || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize))
770 return 0;
771 } while(fourCC != FOURCC_FMT);
772 if(!ReadBin4(istream, src->mPath.data(), order, 2, &format)
773 || !ReadBin4(istream, src->mPath.data(), order, 2, &channels)
774 || !ReadBin4(istream, src->mPath.data(), order, 4, &rate)
775 || !ReadBin4(istream, src->mPath.data(), order, 4, &dummy)
776 || !ReadBin4(istream, src->mPath.data(), order, 2, &block))
777 return 0;
778 block /= channels;
779 if(chunkSize > 14)
781 if(!ReadBin4(istream, src->mPath.data(), order, 2, &size))
782 return 0;
783 size /= 8;
784 if(block > size)
785 size = block;
787 else
788 size = block;
789 if(format == WAVE_FORMAT_EXTENSIBLE)
791 istream.seekg(2, std::ios::cur);
792 if(!ReadBin4(istream, src->mPath.data(), order, 2, &bits))
793 return 0;
794 if(bits == 0)
795 bits = 8 * size;
796 istream.seekg(4, std::ios::cur);
797 if(!ReadBin4(istream, src->mPath.data(), order, 2, &format))
798 return 0;
799 istream.seekg(static_cast<int>(chunkSize - 26), std::ios::cur);
801 else
803 bits = 8 * size;
804 if(chunkSize > 14)
805 istream.seekg(static_cast<int>(chunkSize - 16), std::ios::cur);
806 else
807 istream.seekg(static_cast<int>(chunkSize - 14), std::ios::cur);
809 if(format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_IEEE_FLOAT)
811 fprintf(stderr, "\nError: Unsupported WAVE format in file '%s'.\n", src->mPath.data());
812 return 0;
814 if(src->mChannel >= channels)
816 fprintf(stderr, "\nError: Missing source channel in WAVE file '%s'.\n", src->mPath.data());
817 return 0;
819 if(rate != hrirRate)
821 fprintf(stderr, "\nError: Mismatched source sample rate in WAVE file '%s'.\n",
822 src->mPath.data());
823 return 0;
825 if(format == WAVE_FORMAT_PCM)
827 if(size < 2 || size > 4)
829 fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n",
830 src->mPath.data());
831 return 0;
833 if(bits < 16 || bits > (8*size))
835 fprintf(stderr, "\nError: Bad significant bits in WAVE file '%s'.\n",
836 src->mPath.data());
837 return 0;
839 src->mType = ET_INT;
841 else
843 if(size != 4 && size != 8)
845 fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n",
846 src->mPath.data());
847 return 0;
849 src->mType = ET_FP;
851 src->mSize = size;
852 src->mBits = static_cast<int>(bits);
853 src->mSkip = channels;
854 return 1;
857 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
858 auto ReadWaveData(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
859 const al::span<double> hrir) -> int
861 auto pre = static_cast<int>(src->mSize * src->mChannel);
862 auto post = static_cast<int>(src->mSize * (src->mSkip - src->mChannel - 1));
863 auto skip = int{0};
864 for(size_t i{0};i < hrir.size();++i)
866 skip += pre;
867 if(skip > 0)
868 istream.seekg(skip, std::ios::cur);
869 if(!ReadBinAsDouble(istream, src->mPath.data(), order, src->mType, src->mSize, src->mBits,
870 &hrir[i]))
871 return 0;
872 skip = post;
874 if(skip > 0)
875 istream.seekg(skip, std::ios::cur);
876 return 1;
879 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
880 // doubles.
881 auto ReadWaveList(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
882 const al::span<double> hrir) -> int
884 uint32_t fourCC, chunkSize, listSize, count;
885 uint block, skip, offset, i;
886 double lastSample;
888 for(;;)
890 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
891 || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize))
892 return 0;
894 if(fourCC == FOURCC_DATA)
896 block = src->mSize * src->mSkip;
897 count = chunkSize / block;
898 if(count < (src->mOffset + hrir.size()))
900 fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath.data());
901 return 0;
903 using off_type = std::istream::off_type;
904 istream.seekg(off_type(src->mOffset) * off_type(block), std::ios::cur);
905 if(!ReadWaveData(istream, src, order, hrir))
906 return 0;
907 return 1;
909 if(fourCC == FOURCC_LIST)
911 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC))
912 return 0;
913 chunkSize -= 4;
914 if(fourCC == FOURCC_WAVL)
915 break;
917 if(chunkSize > 0)
918 istream.seekg(static_cast<long>(chunkSize), std::ios::cur);
920 listSize = chunkSize;
921 block = src->mSize * src->mSkip;
922 skip = src->mOffset;
923 offset = 0;
924 lastSample = 0.0;
925 while(offset < hrir.size() && listSize > 8)
927 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
928 || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize))
929 return 0;
930 listSize -= 8 + chunkSize;
931 if(fourCC == FOURCC_DATA)
933 count = chunkSize / block;
934 if(count > skip)
936 using off_type = std::istream::off_type;
937 istream.seekg(off_type(skip) * off_type(block), std::ios::cur);
938 chunkSize -= skip * block;
939 count -= skip;
940 skip = 0;
941 if(count > (hrir.size() - offset))
942 count = static_cast<uint>(hrir.size() - offset);
943 if(!ReadWaveData(istream, src, order, hrir.subspan(offset, count)))
944 return 0;
945 chunkSize -= count * block;
946 offset += count;
947 lastSample = hrir[offset - 1];
949 else
951 skip -= count;
952 count = 0;
955 else if(fourCC == FOURCC_SLNT)
957 if(!ReadBin4(istream, src->mPath.data(), order, 4, &count))
958 return 0;
959 chunkSize -= 4;
960 if(count > skip)
962 count -= skip;
963 skip = 0;
964 if(count > (hrir.size() - offset))
965 count = static_cast<uint>(hrir.size() - offset);
966 for(i = 0; i < count; i ++)
967 hrir[offset + i] = lastSample;
968 offset += count;
970 else
972 skip -= count;
973 count = 0;
976 if(chunkSize > 0)
977 istream.seekg(static_cast<long>(chunkSize), std::ios::cur);
979 if(offset < hrir.size())
981 fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath.data());
982 return 0;
984 return 1;
987 // Load a source HRIR from an ASCII text file containing a list of elements
988 // separated by whitespace or common list operators (',', ';', ':', '|').
989 auto LoadAsciiSource(std::istream &istream, const SourceRefT *src, const al::span<double> hrir) -> int
991 TokenReaderT tr{istream};
993 TrSetup({}, {}, &tr);
994 for(uint i{0};i < src->mOffset;++i)
996 double dummy{};
997 if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, static_cast<uint>(src->mBits),
998 &dummy))
999 return 0;
1001 for(size_t i{0};i < hrir.size();++i)
1003 if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, static_cast<uint>(src->mBits),
1004 &hrir[i]))
1005 return 0;
1006 for(uint j{0};j < src->mSkip;++j)
1008 double dummy{};
1009 if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType,
1010 static_cast<uint>(src->mBits), &dummy))
1011 return 0;
1014 return 1;
1017 // Load a source HRIR from a binary file.
1018 auto LoadBinarySource(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
1019 const al::span<double> hrir) -> int
1021 istream.seekg(static_cast<long>(src->mOffset), std::ios::beg);
1022 for(size_t i{0};i < hrir.size();++i)
1024 if(!ReadBinAsDouble(istream, src->mPath.data(), order, src->mType, src->mSize, src->mBits,
1025 &hrir[i]))
1026 return 0;
1027 if(src->mSkip > 0)
1028 istream.seekg(static_cast<long>(src->mSkip), std::ios::cur);
1030 return 1;
1033 // Load a source HRIR from a RIFF/RIFX WAVE file.
1034 auto LoadWaveSource(std::istream &istream, SourceRefT *src, const uint hrirRate,
1035 const al::span<double> hrir) -> int
1037 uint32_t fourCC, dummy;
1038 ByteOrderT order;
1040 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
1041 || !ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &dummy))
1042 return 0;
1043 if(fourCC == FOURCC_RIFF)
1044 order = BO_LITTLE;
1045 else if(fourCC == FOURCC_RIFX)
1046 order = BO_BIG;
1047 else
1049 fprintf(stderr, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src->mPath.data());
1050 return 0;
1053 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC))
1054 return 0;
1055 if(fourCC != FOURCC_WAVE)
1057 fprintf(stderr, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src->mPath.data());
1058 return 0;
1060 if(!ReadWaveFormat(istream, order, hrirRate, src))
1061 return 0;
1062 if(!ReadWaveList(istream, src, order, hrir))
1063 return 0;
1064 return 1;
1068 struct SofaEasyDeleter {
1069 void operator()(gsl::owner<MYSOFA_EASY*> sofa)
1071 if(sofa->neighborhood) mysofa_neighborhood_free(sofa->neighborhood);
1072 if(sofa->lookup) mysofa_lookup_free(sofa->lookup);
1073 if(sofa->hrtf) mysofa_free(sofa->hrtf);
1074 delete sofa;
1077 using SofaEasyPtr = std::unique_ptr<MYSOFA_EASY,SofaEasyDeleter>;
1079 struct SofaCacheEntry {
1080 std::string mName;
1081 uint mSampleRate{};
1082 SofaEasyPtr mSofa;
1084 std::vector<SofaCacheEntry> gSofaCache;
1086 // Load a Spatially Oriented Format for Accoustics (SOFA) file.
1087 auto LoadSofaFile(SourceRefT *src, const uint hrirRate, const uint n) -> MYSOFA_EASY*
1089 const std::string_view srcname{src->mPath.data()};
1090 auto iter = std::find_if(gSofaCache.begin(), gSofaCache.end(),
1091 [srcname,hrirRate](SofaCacheEntry &entry) -> bool
1092 { return entry.mName == srcname && entry.mSampleRate == hrirRate; });
1093 if(iter != gSofaCache.end()) return iter->mSofa.get();
1095 SofaEasyPtr sofa{new(std::nothrow) MYSOFA_EASY{}};
1096 if(!sofa)
1098 fprintf(stderr, "\nError: Out of memory.\n");
1099 return nullptr;
1101 sofa->lookup = nullptr;
1102 sofa->neighborhood = nullptr;
1104 int err;
1105 sofa->hrtf = mysofa_load(src->mPath.data(), &err);
1106 if(!sofa->hrtf)
1108 fprintf(stderr, "\nError: Could not load source file '%s' (error: %d).\n",
1109 src->mPath.data(), err);
1110 return nullptr;
1112 /* NOTE: Some valid SOFA files are failing this check. */
1113 err = mysofa_check(sofa->hrtf);
1114 if(err != MYSOFA_OK)
1115 fprintf(stderr, "\nWarning: Supposedly malformed source file '%s' (error: %d).\n",
1116 src->mPath.data(), err);
1117 if((src->mOffset + n) > sofa->hrtf->N)
1119 fprintf(stderr, "\nError: Not enough samples in SOFA file '%s'.\n", src->mPath.data());
1120 return nullptr;
1122 if(src->mChannel >= sofa->hrtf->R)
1124 fprintf(stderr, "\nError: Missing source receiver in SOFA file '%s'.\n",src->mPath.data());
1125 return nullptr;
1127 mysofa_tocartesian(sofa->hrtf);
1128 sofa->lookup = mysofa_lookup_init(sofa->hrtf);
1129 if(sofa->lookup == nullptr)
1131 fprintf(stderr, "\nError: Out of memory.\n");
1132 return nullptr;
1134 gSofaCache.emplace_back(SofaCacheEntry{std::string{srcname}, hrirRate, std::move(sofa)});
1135 return gSofaCache.back().mSofa.get();
1138 // Copies the HRIR data from a particular SOFA measurement.
1139 void ExtractSofaHrir(const MYSOFA_HRTF *hrtf, const size_t index, const size_t channel,
1140 const size_t offset, const al::span<double> hrir)
1142 const auto irValues = al::span{hrtf->DataIR.values, hrtf->DataIR.elements}
1143 .subspan((index*hrtf->R + channel)*hrtf->N + offset);
1144 std::copy_n(irValues.cbegin(), hrir.size(), hrir.begin());
1147 // Load a source HRIR from a Spatially Oriented Format for Accoustics (SOFA)
1148 // file.
1149 auto LoadSofaSource(SourceRefT *src, const uint hrirRate, const al::span<double> hrir) -> int
1151 MYSOFA_EASY *sofa{LoadSofaFile(src, hrirRate, static_cast<uint>(hrir.size()))};
1152 if(sofa == nullptr) return 0;
1154 /* NOTE: At some point it may be beneficial or necessary to consider the
1155 various coordinate systems, listener/source orientations, and
1156 directional vectors defined in the SOFA file.
1158 std::array target{
1159 static_cast<float>(src->mAzimuth),
1160 static_cast<float>(src->mElevation),
1161 static_cast<float>(src->mRadius)
1163 mysofa_s2c(target.data());
1165 int nearest{mysofa_lookup(sofa->lookup, target.data())};
1166 if(nearest < 0)
1168 fprintf(stderr, "\nError: Lookup failed in source file '%s'.\n", src->mPath.data());
1169 return 0;
1172 al::span<float,3> coords = al::span{sofa->hrtf->SourcePosition.values, sofa->hrtf->M*3_uz}
1173 .subspan(static_cast<uint>(nearest)*3_uz).first<3>();
1174 if(std::abs(coords[0] - target[0]) > 0.001 || std::abs(coords[1] - target[1]) > 0.001
1175 || std::abs(coords[2] - target[2]) > 0.001)
1177 fprintf(stderr, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n",
1178 src->mRadius, src->mElevation, src->mAzimuth, src->mPath.data());
1179 target[0] = coords[0];
1180 target[1] = coords[1];
1181 target[2] = coords[2];
1182 mysofa_c2s(target.data());
1183 fprintf(stderr, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target[2],
1184 target[1], target[0]);
1185 return 0;
1188 ExtractSofaHrir(sofa->hrtf, static_cast<uint>(nearest), src->mChannel, src->mOffset, hrir);
1190 return 1;
1193 // Load a source HRIR from a supported file type.
1194 auto LoadSource(SourceRefT *src, const uint hrirRate, const al::span<double> hrir) -> int
1196 std::unique_ptr<std::istream> istream;
1197 if(src->mFormat != SF_SOFA)
1199 if(src->mFormat == SF_ASCII)
1200 istream = std::make_unique<std::ifstream>(std::filesystem::u8path(src->mPath.data()));
1201 else
1202 istream = std::make_unique<std::ifstream>(std::filesystem::u8path(src->mPath.data()),
1203 std::ios::binary);
1204 if(!istream->good())
1206 fprintf(stderr, "\nError: Could not open source file '%s'.\n", src->mPath.data());
1207 return 0;
1211 switch(src->mFormat)
1213 case SF_ASCII: return LoadAsciiSource(*istream, src, hrir);
1214 case SF_BIN_LE: return LoadBinarySource(*istream, src, BO_LITTLE, hrir);
1215 case SF_BIN_BE: return LoadBinarySource(*istream, src, BO_BIG, hrir);
1216 case SF_WAVE: return LoadWaveSource(*istream, src, hrirRate, hrir);
1217 case SF_SOFA: return LoadSofaSource(src, hrirRate, hrir);
1218 case SF_NONE: break;
1220 return 0;
1224 // Match the channel type from a given identifier.
1225 auto MatchChannelType(const char *ident) -> ChannelTypeT
1227 if(al::strcasecmp(ident, "mono") == 0)
1228 return CT_MONO;
1229 if(al::strcasecmp(ident, "stereo") == 0)
1230 return CT_STEREO;
1231 return CT_NONE;
1235 // Process the data set definition to read and validate the data set metrics.
1236 auto ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint truncSize,
1237 const ChannelModeT chanMode, HrirDataT *hData) -> int
1239 int hasRate = 0, hasType = 0, hasPoints = 0, hasRadius = 0;
1240 int hasDistance = 0, hasAzimuths = 0;
1241 std::array<char,MaxIdentLen+1> ident;
1242 uint line, col;
1243 double fpVal;
1244 uint points;
1245 int intVal;
1246 std::array<double,MAX_FD_COUNT> distances;
1247 uint fdCount = 0;
1248 std::array<uint,MAX_FD_COUNT> evCounts;
1249 auto azCounts = std::vector<std::array<uint,MAX_EV_COUNT>>(MAX_FD_COUNT);
1250 for(auto &azs : azCounts) azs.fill(0u);
1252 TrIndication(tr, &line, &col);
1253 while(TrIsIdent(tr))
1255 TrIndication(tr, &line, &col);
1256 if(!TrReadIdent(tr, ident))
1257 return 0;
1258 if(al::strcasecmp(ident.data(), "rate") == 0)
1260 if(hasRate)
1262 TrErrorAt(tr, line, col, "Redefinition of 'rate'.\n");
1263 return 0;
1265 if(!TrReadOperator(tr, "="))
1266 return 0;
1267 if(!TrReadInt(tr, MIN_RATE, MAX_RATE, &intVal))
1268 return 0;
1269 hData->mIrRate = static_cast<uint>(intVal);
1270 hasRate = 1;
1272 else if(al::strcasecmp(ident.data(), "type") == 0)
1274 std::array<char,MaxIdentLen+1> type;
1276 if(hasType)
1278 TrErrorAt(tr, line, col, "Redefinition of 'type'.\n");
1279 return 0;
1281 if(!TrReadOperator(tr, "="))
1282 return 0;
1284 if(!TrReadIdent(tr, type))
1285 return 0;
1286 hData->mChannelType = MatchChannelType(type.data());
1287 if(hData->mChannelType == CT_NONE)
1289 TrErrorAt(tr, line, col, "Expected a channel type.\n");
1290 return 0;
1292 if(hData->mChannelType == CT_STEREO)
1294 if(chanMode == CM_ForceMono)
1295 hData->mChannelType = CT_MONO;
1297 hasType = 1;
1299 else if(al::strcasecmp(ident.data(), "points") == 0)
1301 if(hasPoints)
1303 TrErrorAt(tr, line, col, "Redefinition of 'points'.\n");
1304 return 0;
1306 if(!TrReadOperator(tr, "="))
1307 return 0;
1308 TrIndication(tr, &line, &col);
1309 if(!TrReadInt(tr, MIN_POINTS, MAX_POINTS, &intVal))
1310 return 0;
1311 points = static_cast<uint>(intVal);
1312 if(fftSize > 0 && points > fftSize)
1314 TrErrorAt(tr, line, col, "Value exceeds the overridden FFT size.\n");
1315 return 0;
1317 if(points < truncSize)
1319 TrErrorAt(tr, line, col, "Value is below the truncation size.\n");
1320 return 0;
1322 hData->mIrPoints = points;
1323 hData->mFftSize = fftSize;
1324 hData->mIrSize = 1 + (fftSize / 2);
1325 if(points > hData->mIrSize)
1326 hData->mIrSize = points;
1327 hasPoints = 1;
1329 else if(al::strcasecmp(ident.data(), "radius") == 0)
1331 if(hasRadius)
1333 TrErrorAt(tr, line, col, "Redefinition of 'radius'.\n");
1334 return 0;
1336 if(!TrReadOperator(tr, "="))
1337 return 0;
1338 if(!TrReadFloat(tr, MinRadius, MaxRadius, &fpVal))
1339 return 0;
1340 hData->mRadius = fpVal;
1341 hasRadius = 1;
1343 else if(al::strcasecmp(ident.data(), "distance") == 0)
1345 uint count = 0;
1347 if(hasDistance)
1349 TrErrorAt(tr, line, col, "Redefinition of 'distance'.\n");
1350 return 0;
1352 if(!TrReadOperator(tr, "="))
1353 return 0;
1355 for(;;)
1357 if(!TrReadFloat(tr, MIN_DISTANCE, MAX_DISTANCE, &fpVal))
1358 return 0;
1359 if(count > 0 && fpVal <= distances[count - 1])
1361 TrError(tr, "Distances are not ascending.\n");
1362 return 0;
1364 distances[count++] = fpVal;
1365 if(!TrIsOperator(tr, ","))
1366 break;
1367 if(count >= MAX_FD_COUNT)
1369 TrError(tr, "Exceeded the maximum of %d fields.\n", MAX_FD_COUNT);
1370 return 0;
1372 TrReadOperator(tr, ",");
1374 if(fdCount != 0 && count != fdCount)
1376 TrError(tr, "Did not match the specified number of %d fields.\n", fdCount);
1377 return 0;
1379 fdCount = count;
1380 hasDistance = 1;
1382 else if(al::strcasecmp(ident.data(), "azimuths") == 0)
1384 uint count = 0;
1386 if(hasAzimuths)
1388 TrErrorAt(tr, line, col, "Redefinition of 'azimuths'.\n");
1389 return 0;
1391 if(!TrReadOperator(tr, "="))
1392 return 0;
1394 evCounts[0] = 0;
1395 for(;;)
1397 if(!TrReadInt(tr, MIN_AZ_COUNT, MAX_AZ_COUNT, &intVal))
1398 return 0;
1399 azCounts[count][evCounts[count]++] = static_cast<uint>(intVal);
1400 if(TrIsOperator(tr, ","))
1402 if(evCounts[count] >= MAX_EV_COUNT)
1404 TrError(tr, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT);
1405 return 0;
1407 TrReadOperator(tr, ",");
1409 else
1411 if(evCounts[count] < MIN_EV_COUNT)
1413 TrErrorAt(tr, line, col, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT);
1414 return 0;
1416 if(azCounts[count][0] != 1 || azCounts[count][evCounts[count] - 1] != 1)
1418 TrError(tr, "Poles are not singular for field %d.\n", count - 1);
1419 return 0;
1421 count++;
1422 if(!TrIsOperator(tr, ";"))
1423 break;
1425 if(count >= MAX_FD_COUNT)
1427 TrError(tr, "Exceeded the maximum number of %d fields.\n", MAX_FD_COUNT);
1428 return 0;
1430 evCounts[count] = 0;
1431 TrReadOperator(tr, ";");
1434 if(fdCount != 0 && count != fdCount)
1436 TrError(tr, "Did not match the specified number of %d fields.\n", fdCount);
1437 return 0;
1439 fdCount = count;
1440 hasAzimuths = 1;
1442 else
1444 TrErrorAt(tr, line, col, "Expected a metric name.\n");
1445 return 0;
1447 TrSkipWhitespace(tr);
1449 if(!(hasRate && hasPoints && hasRadius && hasDistance && hasAzimuths))
1451 TrErrorAt(tr, line, col, "Expected a metric name.\n");
1452 return 0;
1454 if(distances[0] < hData->mRadius)
1456 TrError(tr, "Distance cannot start below head radius.\n");
1457 return 0;
1459 if(hData->mChannelType == CT_NONE)
1460 hData->mChannelType = CT_MONO;
1461 const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>();
1462 if(!PrepareHrirData(al::span{distances}.first(fdCount), evCounts, azs, hData))
1464 fprintf(stderr, "Error: Out of memory.\n");
1465 exit(-1);
1467 return 1;
1470 // Parse an index triplet from the data set definition.
1471 auto ReadIndexTriplet(TokenReaderT *tr, const HrirDataT *hData, uint *fi, uint *ei, uint *ai)->int
1473 int intVal;
1475 if(hData->mFds.size() > 1)
1477 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds.size()-1), &intVal))
1478 return 0;
1479 *fi = static_cast<uint>(intVal);
1480 if(!TrReadOperator(tr, ","))
1481 return 0;
1483 else
1485 *fi = 0;
1487 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds[*fi].mEvs.size()-1), &intVal))
1488 return 0;
1489 *ei = static_cast<uint>(intVal);
1490 if(!TrReadOperator(tr, ","))
1491 return 0;
1492 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds[*fi].mEvs[*ei].mAzs.size()-1), &intVal))
1493 return 0;
1494 *ai = static_cast<uint>(intVal);
1495 return 1;
1498 // Match the source format from a given identifier.
1499 auto MatchSourceFormat(const char *ident) -> SourceFormatT
1501 if(al::strcasecmp(ident, "ascii") == 0)
1502 return SF_ASCII;
1503 if(al::strcasecmp(ident, "bin_le") == 0)
1504 return SF_BIN_LE;
1505 if(al::strcasecmp(ident, "bin_be") == 0)
1506 return SF_BIN_BE;
1507 if(al::strcasecmp(ident, "wave") == 0)
1508 return SF_WAVE;
1509 if(al::strcasecmp(ident, "sofa") == 0)
1510 return SF_SOFA;
1511 return SF_NONE;
1514 // Match the source element type from a given identifier.
1515 auto MatchElementType(const char *ident) -> ElementTypeT
1517 if(al::strcasecmp(ident, "int") == 0)
1518 return ET_INT;
1519 if(al::strcasecmp(ident, "fp") == 0)
1520 return ET_FP;
1521 return ET_NONE;
1524 // Parse and validate a source reference from the data set definition.
1525 auto ReadSourceRef(TokenReaderT *tr, SourceRefT *src) -> int
1527 std::array<char,MaxIdentLen+1> ident;
1528 uint line, col;
1529 double fpVal;
1530 int intVal;
1532 TrIndication(tr, &line, &col);
1533 if(!TrReadIdent(tr, ident))
1534 return 0;
1535 src->mFormat = MatchSourceFormat(ident.data());
1536 if(src->mFormat == SF_NONE)
1538 TrErrorAt(tr, line, col, "Expected a source format.\n");
1539 return 0;
1541 if(!TrReadOperator(tr, "("))
1542 return 0;
1543 if(src->mFormat == SF_SOFA)
1545 if(!TrReadFloat(tr, MIN_DISTANCE, MAX_DISTANCE, &fpVal))
1546 return 0;
1547 src->mRadius = fpVal;
1548 if(!TrReadOperator(tr, ","))
1549 return 0;
1550 if(!TrReadFloat(tr, -90.0, 90.0, &fpVal))
1551 return 0;
1552 src->mElevation = fpVal;
1553 if(!TrReadOperator(tr, ","))
1554 return 0;
1555 if(!TrReadFloat(tr, -360.0, 360.0, &fpVal))
1556 return 0;
1557 src->mAzimuth = fpVal;
1558 if(!TrReadOperator(tr, ":"))
1559 return 0;
1560 if(!TrReadInt(tr, 0, MaxWaveChannels, &intVal))
1561 return 0;
1562 src->mType = ET_NONE;
1563 src->mSize = 0;
1564 src->mBits = 0;
1565 src->mChannel = static_cast<uint>(intVal);
1566 src->mSkip = 0;
1568 else if(src->mFormat == SF_WAVE)
1570 if(!TrReadInt(tr, 0, MaxWaveChannels, &intVal))
1571 return 0;
1572 src->mType = ET_NONE;
1573 src->mSize = 0;
1574 src->mBits = 0;
1575 src->mChannel = static_cast<uint>(intVal);
1576 src->mSkip = 0;
1578 else
1580 TrIndication(tr, &line, &col);
1581 if(!TrReadIdent(tr, ident))
1582 return 0;
1583 src->mType = MatchElementType(ident.data());
1584 if(src->mType == ET_NONE)
1586 TrErrorAt(tr, line, col, "Expected a source element type.\n");
1587 return 0;
1589 if(src->mFormat == SF_BIN_LE || src->mFormat == SF_BIN_BE)
1591 if(!TrReadOperator(tr, ","))
1592 return 0;
1593 if(src->mType == ET_INT)
1595 if(!TrReadInt(tr, MinBinSize, MaxBinSize, &intVal))
1596 return 0;
1597 src->mSize = static_cast<uint>(intVal);
1598 if(!TrIsOperator(tr, ","))
1599 src->mBits = static_cast<int>(8*src->mSize);
1600 else
1602 TrReadOperator(tr, ",");
1603 TrIndication(tr, &line, &col);
1604 if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal))
1605 return 0;
1606 if(std::abs(intVal) < int{MinBinSize}*8 || static_cast<uint>(std::abs(intVal)) > (8*src->mSize))
1608 TrErrorAt(tr, line, col, "Expected a value of (+/-) %d to %d.\n", MinBinSize*8, 8*src->mSize);
1609 return 0;
1611 src->mBits = intVal;
1614 else
1616 TrIndication(tr, &line, &col);
1617 if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal))
1618 return 0;
1619 if(intVal != 4 && intVal != 8)
1621 TrErrorAt(tr, line, col, "Expected a value of 4 or 8.\n");
1622 return 0;
1624 src->mSize = static_cast<uint>(intVal);
1625 src->mBits = 0;
1628 else if(src->mFormat == SF_ASCII && src->mType == ET_INT)
1630 if(!TrReadOperator(tr, ","))
1631 return 0;
1632 if(!TrReadInt(tr, MinASCIIBits, MaxASCIIBits, &intVal))
1633 return 0;
1634 src->mSize = 0;
1635 src->mBits = intVal;
1637 else
1639 src->mSize = 0;
1640 src->mBits = 0;
1643 if(!TrIsOperator(tr, ";"))
1644 src->mSkip = 0;
1645 else
1647 TrReadOperator(tr, ";");
1648 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1649 return 0;
1650 src->mSkip = static_cast<uint>(intVal);
1653 if(!TrReadOperator(tr, ")"))
1654 return 0;
1655 if(TrIsOperator(tr, "@"))
1657 TrReadOperator(tr, "@");
1658 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1659 return 0;
1660 src->mOffset = static_cast<uint>(intVal);
1662 else
1663 src->mOffset = 0;
1664 if(!TrReadOperator(tr, ":"))
1665 return 0;
1666 if(!TrReadString(tr, src->mPath))
1667 return 0;
1668 return 1;
1671 // Parse and validate a SOFA source reference from the data set definition.
1672 auto ReadSofaRef(TokenReaderT *tr, SourceRefT *src) -> int
1674 std::array<char,MaxIdentLen+1> ident;
1675 uint line, col;
1676 int intVal;
1678 TrIndication(tr, &line, &col);
1679 if(!TrReadIdent(tr, ident))
1680 return 0;
1681 src->mFormat = MatchSourceFormat(ident.data());
1682 if(src->mFormat != SF_SOFA)
1684 TrErrorAt(tr, line, col, "Expected the SOFA source format.\n");
1685 return 0;
1688 src->mType = ET_NONE;
1689 src->mSize = 0;
1690 src->mBits = 0;
1691 src->mChannel = 0;
1692 src->mSkip = 0;
1694 if(TrIsOperator(tr, "@"))
1696 TrReadOperator(tr, "@");
1697 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1698 return 0;
1699 src->mOffset = static_cast<uint>(intVal);
1701 else
1702 src->mOffset = 0;
1703 if(!TrReadOperator(tr, ":"))
1704 return 0;
1705 if(!TrReadString(tr, src->mPath))
1706 return 0;
1707 return 1;
1710 // Match the target ear (index) from a given identifier.
1711 auto MatchTargetEar(const char *ident) -> int
1713 if(al::strcasecmp(ident, "left") == 0)
1714 return 0;
1715 if(al::strcasecmp(ident, "right") == 0)
1716 return 1;
1717 return -1;
1720 // Calculate the onset time of an HRIR and average it with any existing
1721 // timing for its field, elevation, azimuth, and ear.
1722 constexpr int OnsetRateMultiple{10};
1723 auto AverageHrirOnset(PPhaseResampler &rs, al::span<double> upsampled, const uint rate,
1724 const al::span<const double> hrir, const double f, const double onset) -> double
1726 rs.process(hrir, upsampled);
1728 auto abs_lt = [](const double &lhs, const double &rhs) -> bool
1729 { return std::abs(lhs) < std::abs(rhs); };
1730 auto iter = std::max_element(upsampled.cbegin(), upsampled.cend(), abs_lt);
1731 return Lerp(onset, static_cast<double>(std::distance(upsampled.cbegin(), iter))/(10*rate), f);
1734 // Calculate the magnitude response of an HRIR and average it with any
1735 // existing responses for its field, elevation, azimuth, and ear.
1736 void AverageHrirMagnitude(const uint fftSize, const al::span<const double> hrir, const double f,
1737 const al::span<double> mag)
1739 const uint m{1 + (fftSize/2)};
1740 std::vector<complex_d> h(fftSize);
1741 std::vector<double> r(m);
1743 auto hiter = std::copy(hrir.cbegin(), hrir.cend(), h.begin());
1744 std::fill(hiter, h.end(), 0.0);
1745 forward_fft(h);
1746 MagnitudeResponse(h, r);
1747 for(uint i{0};i < m;++i)
1748 mag[i] = Lerp(mag[i], r[i], f);
1751 // Process the list of sources in the data set definition.
1752 auto ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate) -> int
1754 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
1755 hData->mHrirsBase.resize(size_t{channels} * hData->mIrCount * hData->mIrSize);
1756 const auto hrirs = al::span<double>{hData->mHrirsBase};
1757 auto hrir = std::vector<double>(hData->mIrSize);
1758 uint line, col, fi, ei, ai;
1760 std::vector<double> onsetSamples(size_t{OnsetRateMultiple} * hData->mIrPoints);
1761 PPhaseResampler onsetResampler;
1762 onsetResampler.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate);
1764 std::optional<PPhaseResampler> resampler;
1765 if(outRate && outRate != hData->mIrRate)
1766 resampler.emplace().init(hData->mIrRate, outRate);
1767 const double rateScale{outRate ? static_cast<double>(outRate) / hData->mIrRate : 1.0};
1768 const uint irPoints{outRate
1769 ? std::min(static_cast<uint>(std::ceil(hData->mIrPoints*rateScale)), hData->mIrPoints)
1770 : hData->mIrPoints};
1772 printf("Loading sources...");
1773 fflush(stdout);
1774 int count{0};
1775 while(TrIsOperator(tr, "["))
1777 std::array factor{1.0, 1.0};
1779 TrIndication(tr, &line, &col);
1780 TrReadOperator(tr, "[");
1782 if(TrIsOperator(tr, "*"))
1784 TrReadOperator(tr, "*");
1785 if(!TrReadOperator(tr, "]") || !TrReadOperator(tr, "="))
1786 return 0;
1788 TrIndication(tr, &line, &col);
1789 SourceRefT src{};
1790 if(!ReadSofaRef(tr, &src))
1791 return 0;
1793 if(hData->mChannelType == CT_STEREO)
1795 std::array<char,MaxIdentLen+1> type{};
1797 if(!TrReadIdent(tr, type))
1798 return 0;
1800 const ChannelTypeT channelType{MatchChannelType(type.data())};
1801 switch(channelType)
1803 case CT_NONE:
1804 TrErrorAt(tr, line, col, "Expected a channel type.\n");
1805 return 0;
1806 case CT_MONO:
1807 src.mChannel = 0;
1808 break;
1809 case CT_STEREO:
1810 src.mChannel = 1;
1811 break;
1814 else
1816 std::array<char,MaxIdentLen+1> type{};
1817 if(!TrReadIdent(tr, type))
1818 return 0;
1820 ChannelTypeT channelType{MatchChannelType(type.data())};
1821 if(channelType != CT_MONO)
1823 TrErrorAt(tr, line, col, "Expected a mono channel type.\n");
1824 return 0;
1826 src.mChannel = 0;
1829 MYSOFA_EASY *sofa{LoadSofaFile(&src, hData->mIrRate, hData->mIrPoints)};
1830 if(!sofa) return 0;
1832 const auto srcPosValues = al::span{sofa->hrtf->SourcePosition.values,
1833 sofa->hrtf->M*3_uz};
1834 for(uint si{0};si < sofa->hrtf->M;++si)
1836 printf("\rLoading sources... %d of %d", si+1, sofa->hrtf->M);
1837 fflush(stdout);
1839 std::array aer{srcPosValues[3_uz*si], srcPosValues[3_uz*si + 1],
1840 srcPosValues[3_uz*si + 2]};
1841 mysofa_c2s(aer.data());
1843 if(std::fabs(aer[1]) >= 89.999f)
1844 aer[0] = 0.0f;
1845 else
1846 aer[0] = std::fmod(360.0f - aer[0], 360.0f);
1848 auto field = std::find_if(hData->mFds.cbegin(), hData->mFds.cend(),
1849 [&aer](const HrirFdT &fld) -> bool
1850 { return (std::abs(aer[2] - fld.mDistance) < 0.001); });
1851 if(field == hData->mFds.cend())
1852 continue;
1853 fi = static_cast<uint>(std::distance(hData->mFds.cbegin(), field));
1855 const double evscale{180.0 / static_cast<double>(field->mEvs.size()-1)};
1856 double ef{(90.0 + aer[1]) / evscale};
1857 ei = static_cast<uint>(std::round(ef));
1858 ef = (ef - ei) * evscale;
1859 if(std::abs(ef) >= 0.1)
1860 continue;
1862 const double azscale{360.0 / static_cast<double>(field->mEvs[ei].mAzs.size())};
1863 double af{aer[0] / azscale};
1864 ai = static_cast<uint>(std::round(af));
1865 af = (af - ai) * azscale;
1866 ai %= static_cast<uint>(field->mEvs[ei].mAzs.size());
1867 if(std::abs(af) >= 0.1)
1868 continue;
1870 HrirAzT *azd = &field->mEvs[ei].mAzs[ai];
1871 if(!azd->mIrs[0].empty())
1873 TrErrorAt(tr, line, col, "Redefinition of source [ %d, %d, %d ].\n", fi, ei, ai);
1874 return 0;
1877 const auto hrirPoints = al::span{hrir}.first(hData->mIrPoints);
1878 ExtractSofaHrir(sofa->hrtf, si, 0, src.mOffset, hrirPoints);
1879 azd->mIrs[0] = hrirs.subspan(size_t{hData->mIrSize}*azd->mIndex, hData->mIrSize);
1880 azd->mDelays[0] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate,
1881 hrirPoints, 1.0, azd->mDelays[0]);
1882 if(resampler)
1883 resampler->process(hrirPoints, hrir);
1884 AverageHrirMagnitude(hData->mFftSize, al::span{hrir}.first(irPoints), 1.0,
1885 azd->mIrs[0]);
1887 if(src.mChannel == 1)
1889 ExtractSofaHrir(sofa->hrtf, si, 1, src.mOffset, hrirPoints);
1890 azd->mIrs[1] = hrirs.subspan(
1891 (size_t{hData->mIrCount}+azd->mIndex) * hData->mIrSize, hData->mIrSize);
1892 azd->mDelays[1] = AverageHrirOnset(onsetResampler, onsetSamples,
1893 hData->mIrRate, hrirPoints, 1.0, azd->mDelays[1]);
1894 if(resampler)
1895 resampler->process(hrirPoints, hrir);
1896 AverageHrirMagnitude(hData->mFftSize, al::span{hrir}.first(irPoints), 1.0,
1897 azd->mIrs[1]);
1900 // TODO: Since some SOFA files contain minimum phase HRIRs,
1901 // it would be beneficial to check for per-measurement delays
1902 // (when available) to reconstruct the HRTDs.
1905 continue;
1908 if(!ReadIndexTriplet(tr, hData, &fi, &ei, &ai))
1909 return 0;
1910 if(!TrReadOperator(tr, "]"))
1911 return 0;
1912 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1914 if(!azd->mIrs[0].empty())
1916 TrErrorAt(tr, line, col, "Redefinition of source.\n");
1917 return 0;
1919 if(!TrReadOperator(tr, "="))
1920 return 0;
1922 while(true)
1924 SourceRefT src{};
1925 if(!ReadSourceRef(tr, &src))
1926 return 0;
1928 // TODO: Would be nice to display 'x of y files', but that would
1929 // require preparing the source refs first to get a total count
1930 // before loading them.
1931 ++count;
1932 printf("\rLoading sources... %d file%s", count, (count==1)?"":"s");
1933 fflush(stdout);
1935 if(!LoadSource(&src, hData->mIrRate, al::span{hrir}.first(hData->mIrPoints)))
1936 return 0;
1938 uint ti{0};
1939 if(hData->mChannelType == CT_STEREO)
1941 std::array<char,MaxIdentLen+1> ident{};
1942 if(!TrReadIdent(tr, ident))
1943 return 0;
1944 ti = static_cast<uint>(MatchTargetEar(ident.data()));
1945 if(static_cast<int>(ti) < 0)
1947 TrErrorAt(tr, line, col, "Expected a target ear.\n");
1948 return 0;
1951 const auto hrirPoints = al::span{hrir}.first(hData->mIrPoints);
1952 azd->mIrs[ti] = hrirs.subspan((ti*size_t{hData->mIrCount}+azd->mIndex)*hData->mIrSize,
1953 hData->mIrSize);
1954 azd->mDelays[ti] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate,
1955 hrirPoints, 1.0/factor[ti], azd->mDelays[ti]);
1956 if(resampler)
1957 resampler->process(hrirPoints, hrir);
1958 AverageHrirMagnitude(hData->mFftSize, al::span{hrir}.subspan(irPoints), 1.0/factor[ti],
1959 azd->mIrs[ti]);
1960 factor[ti] += 1.0;
1961 if(!TrIsOperator(tr, "+"))
1962 break;
1963 TrReadOperator(tr, "+");
1965 if(hData->mChannelType == CT_STEREO)
1967 if(azd->mIrs[0].empty())
1969 TrErrorAt(tr, line, col, "Missing left ear source reference(s).\n");
1970 return 0;
1972 if(azd->mIrs[1].empty())
1974 TrErrorAt(tr, line, col, "Missing right ear source reference(s).\n");
1975 return 0;
1979 printf("\n");
1980 hrir.clear();
1981 if(resampler)
1983 hData->mIrRate = outRate;
1984 hData->mIrPoints = irPoints;
1985 resampler.reset();
1987 for(fi = 0;fi < hData->mFds.size();fi++)
1989 for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
1991 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
1993 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1994 if(!azd->mIrs[0].empty())
1995 break;
1997 if(ai < hData->mFds[fi].mEvs[ei].mAzs.size())
1998 break;
2000 if(ei >= hData->mFds[fi].mEvs.size())
2002 TrError(tr, "Missing source references [ %d, *, * ].\n", fi);
2003 return 0;
2005 hData->mFds[fi].mEvStart = ei;
2006 for(;ei < hData->mFds[fi].mEvs.size();ei++)
2008 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
2010 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
2012 if(azd->mIrs[0].empty())
2014 TrError(tr, "Missing source reference [ %d, %d, %d ].\n", fi, ei, ai);
2015 return 0;
2020 for(uint ti{0};ti < channels;ti++)
2022 for(fi = 0;fi < hData->mFds.size();fi++)
2024 for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
2026 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
2028 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
2029 azd->mIrs[ti] = hrirs.subspan(
2030 (ti*size_t{hData->mIrCount} + azd->mIndex) * hData->mIrSize,
2031 hData->mIrSize);
2036 if(!TrLoad(tr))
2038 gSofaCache.clear();
2039 return 1;
2042 TrError(tr, "Errant data at end of source list.\n");
2043 gSofaCache.clear();
2044 return 0;
2047 } /* namespace */
2049 bool LoadDefInput(std::istream &istream, const al::span<const char> startbytes,
2050 const std::string_view filename, const uint fftSize, const uint truncSize, const uint outRate,
2051 const ChannelModeT chanMode, HrirDataT *hData)
2053 TokenReaderT tr{istream};
2055 TrSetup(startbytes, filename, &tr);
2056 if(!ProcessMetrics(&tr, fftSize, truncSize, chanMode, hData)
2057 || !ProcessSources(&tr, hData, outRate))
2058 return false;
2060 return true;