Check that AltiVec is enabled before using it
[openal-soft.git] / utils / makemhr / loaddef.cpp
blob3c068244281e4e0523adf5789cac7f262b9c4b39
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"
50 #include "sofa-support.h"
52 #include "mysofa.h"
54 namespace {
56 using namespace std::string_view_literals;
58 // Constants for accessing the token reader's ring buffer.
59 constexpr uint TRRingBits{16};
60 constexpr uint TRRingSize{1 << TRRingBits};
61 constexpr uint TRRingMask{TRRingSize - 1};
63 // The token reader's load interval in bytes.
64 constexpr uint TRLoadSize{TRRingSize >> 2};
66 // Token reader state for parsing the data set definition.
67 struct TokenReaderT {
68 std::istream &mIStream;
69 std::string mName{};
70 uint mLine{};
71 uint mColumn{};
72 std::array<char,TRRingSize> mRing{};
73 std::streamsize mIn{};
74 std::streamsize mOut{};
76 TokenReaderT(std::istream &istream) noexcept : mIStream{istream} { }
77 TokenReaderT(const TokenReaderT&) = default;
81 // The limits for the listener's head 'radius' in the data set definition.
82 constexpr double MinRadius{0.05};
83 constexpr double MaxRadius{0.15};
85 // The maximum number of channels that can be addressed for a WAVE file
86 // source listed in the data set definition.
87 constexpr uint MaxWaveChannels{65535};
89 // The limits to the byte size for a binary source listed in the definition
90 // file.
91 enum : uint {
92 MinBinSize = 2,
93 MaxBinSize = 4
96 // The limits to the number of significant bits for an ASCII source listed in
97 // the data set definition.
98 enum : uint {
99 MinASCIIBits = 16,
100 MaxASCIIBits = 32
103 // The four-character-codes for RIFF/RIFX WAVE file chunks.
104 enum : uint {
105 FOURCC_RIFF = 0x46464952, // 'RIFF'
106 FOURCC_RIFX = 0x58464952, // 'RIFX'
107 FOURCC_WAVE = 0x45564157, // 'WAVE'
108 FOURCC_FMT = 0x20746D66, // 'fmt '
109 FOURCC_DATA = 0x61746164, // 'data'
110 FOURCC_LIST = 0x5453494C, // 'LIST'
111 FOURCC_WAVL = 0x6C766177, // 'wavl'
112 FOURCC_SLNT = 0x746E6C73, // 'slnt'
115 // The supported wave formats.
116 enum : uint {
117 WAVE_FORMAT_PCM = 0x0001,
118 WAVE_FORMAT_IEEE_FLOAT = 0x0003,
119 WAVE_FORMAT_EXTENSIBLE = 0xFFFE,
123 enum ByteOrderT {
124 BO_NONE,
125 BO_LITTLE,
126 BO_BIG
129 // Source format for the references listed in the data set definition.
130 enum SourceFormatT {
131 SF_NONE,
132 SF_ASCII, // ASCII text file.
133 SF_BIN_LE, // Little-endian binary file.
134 SF_BIN_BE, // Big-endian binary file.
135 SF_WAVE, // RIFF/RIFX WAVE file.
136 SF_SOFA // Spatially Oriented Format for Accoustics (SOFA) file.
139 // Element types for the references listed in the data set definition.
140 enum ElementTypeT {
141 ET_NONE,
142 ET_INT, // Integer elements.
143 ET_FP // Floating-point elements.
146 // Source reference state used when loading sources.
147 struct SourceRefT {
148 SourceFormatT mFormat;
149 ElementTypeT mType;
150 uint mSize;
151 int mBits;
152 uint mChannel;
153 double mAzimuth;
154 double mElevation;
155 double mRadius;
156 uint mSkip;
157 uint mOffset;
158 std::array<char,MAX_PATH_LEN+1> mPath;
162 /* Whitespace is not significant. It can process tokens as identifiers, numbers
163 * (integer and floating-point), strings, and operators. Strings must be
164 * encapsulated by double-quotes and cannot span multiple lines.
167 // Setup the reader on the given file. The filename can be NULL if no error
168 // output is desired.
169 void TrSetup(const al::span<const char> startbytes, const std::string_view filename,
170 TokenReaderT *tr)
172 std::string_view namepart;
174 if(!filename.empty())
176 const auto fslashpos = filename.rfind('/');
177 const auto bslashpos = filename.rfind('\\');
178 const auto slashpos = (bslashpos >= filename.size()) ? fslashpos :
179 (fslashpos >= filename.size()) ? bslashpos :
180 std::max(fslashpos, bslashpos);
181 if(slashpos < filename.size())
182 namepart = filename.substr(slashpos+1);
185 tr->mName = namepart;
186 tr->mLine = 1;
187 tr->mColumn = 1;
188 tr->mIn = 0;
189 tr->mOut = 0;
191 if(!startbytes.empty())
193 assert(startbytes.size() <= tr->mRing.size());
194 std::copy(startbytes.cbegin(), startbytes.cend(), tr->mRing.begin());
195 tr->mIn += std::streamsize(startbytes.size());
199 // Prime the reader's ring buffer, and return a result indicating that there
200 // is text to process.
201 auto TrLoad(TokenReaderT *tr) -> int
203 std::istream &istream = tr->mIStream;
205 auto toLoad = std::streamsize{TRRingSize} - (tr->mIn - tr->mOut);
206 if(toLoad >= TRLoadSize && istream.good())
208 // Load TRLoadSize (or less if at the end of the file) per read.
209 toLoad = TRLoadSize;
211 const auto in = tr->mIn&TRRingMask;
212 std::streamsize count{TRRingSize - in};
213 if(count < toLoad)
215 istream.read(al::to_address(tr->mRing.begin() + in), count);
216 tr->mIn += istream.gcount();
217 istream.read(tr->mRing.data(), toLoad-count);
218 tr->mIn += istream.gcount();
220 else
222 istream.read(al::to_address(tr->mRing.begin() + in), toLoad);
223 tr->mIn += istream.gcount();
226 if(tr->mOut >= TRRingSize)
228 tr->mOut -= TRRingSize;
229 tr->mIn -= TRRingSize;
232 if(tr->mIn > tr->mOut)
233 return 1;
234 return 0;
237 // Error display routine. Only displays when the base name is not NULL.
238 void TrErrorVA(const TokenReaderT *tr, uint line, uint column, const char *format, va_list argPtr)
240 if(tr->mName.empty())
241 return;
242 fprintf(stderr, "\nError (%s:%u:%u): ", tr->mName.c_str(), line, column);
243 vfprintf(stderr, format, argPtr);
246 // Used to display an error at a saved line/column.
247 void TrErrorAt(const TokenReaderT *tr, uint line, uint column, const char *format, ...)
249 /* NOLINTBEGIN(*-array-to-pointer-decay) */
250 va_list argPtr;
251 va_start(argPtr, format);
252 TrErrorVA(tr, line, column, format, argPtr);
253 va_end(argPtr);
254 /* NOLINTEND(*-array-to-pointer-decay) */
257 // Used to display an error at the current line/column.
258 void TrError(const TokenReaderT *tr, const char *format, ...)
260 /* NOLINTBEGIN(*-array-to-pointer-decay) */
261 va_list argPtr;
262 va_start(argPtr, format);
263 TrErrorVA(tr, tr->mLine, tr->mColumn, format, argPtr);
264 va_end(argPtr);
265 /* NOLINTEND(*-array-to-pointer-decay) */
268 // Skips to the next line.
269 void TrSkipLine(TokenReaderT *tr)
271 char ch;
273 while(TrLoad(tr))
275 ch = tr->mRing[tr->mOut&TRRingMask];
276 tr->mOut++;
277 if(ch == '\n')
279 tr->mLine++;
280 tr->mColumn = 1;
281 break;
283 tr->mColumn ++;
287 // Skips to the next token.
288 auto TrSkipWhitespace(TokenReaderT *tr) -> int
290 while(TrLoad(tr))
292 char ch{tr->mRing[tr->mOut&TRRingMask]};
293 if(isspace(ch))
295 tr->mOut++;
296 if(ch == '\n')
298 tr->mLine++;
299 tr->mColumn = 1;
301 else
302 tr->mColumn++;
304 else if(ch == '#')
305 TrSkipLine(tr);
306 else
307 return 1;
309 return 0;
312 // Get the line and/or column of the next token (or the end of input).
313 void TrIndication(TokenReaderT *tr, uint *line, uint *column)
315 TrSkipWhitespace(tr);
316 if(line) *line = tr->mLine;
317 if(column) *column = tr->mColumn;
320 // Checks to see if a token is (likely to be) an identifier. It does not
321 // display any errors and will not proceed to the next token.
322 auto TrIsIdent(TokenReaderT *tr) -> int
324 if(!TrSkipWhitespace(tr))
325 return 0;
326 char ch{tr->mRing[tr->mOut&TRRingMask]};
327 return ch == '_' || isalpha(ch);
331 // Checks to see if a token is the given operator. It does not display any
332 // errors and will not proceed to the next token.
333 auto TrIsOperator(TokenReaderT *tr, const std::string_view op) -> int
335 if(!TrSkipWhitespace(tr))
336 return 0;
337 auto out = tr->mOut;
338 size_t len{0};
339 while(len < op.size() && out < tr->mIn)
341 if(tr->mRing[out&TRRingMask] != op[len])
342 break;
343 ++len;
344 ++out;
346 if(len == op.size())
347 return 1;
348 return 0;
351 /* The TrRead*() routines obtain the value of a matching token type. They
352 * display type, form, and boundary errors and will proceed to the next
353 * token.
356 // Reads and validates an identifier token.
357 auto TrReadIdent(TokenReaderT *tr) -> std::string
359 auto ret = std::string{};
360 auto col = tr->mColumn;
361 if(TrSkipWhitespace(tr))
363 col = tr->mColumn;
364 auto ch = char{tr->mRing[tr->mOut&TRRingMask]};
365 if(ch == '_' || isalpha(ch))
367 do {
368 ret += ch;
369 tr->mColumn += 1;
370 tr->mOut += 1;
371 if(!TrLoad(tr))
372 break;
373 ch = tr->mRing[tr->mOut&TRRingMask];
374 } while(ch == '_' || std::isdigit(ch) || std::isalpha(ch));
376 return ret;
379 TrErrorAt(tr, tr->mLine, col, "Expected an identifier.\n");
380 ret.clear();
381 return ret;
384 // Reads and validates (including bounds) an integer token.
385 auto TrReadInt(TokenReaderT *tr, const int loBound, const int hiBound, int *value) -> int
387 uint col{tr->mColumn};
388 if(TrSkipWhitespace(tr))
390 col = tr->mColumn;
391 uint len{0};
392 std::array<char,64+1> temp{};
393 char ch{tr->mRing[tr->mOut&TRRingMask]};
394 if(ch == '+' || ch == '-')
396 temp[len] = ch;
397 len++;
398 tr->mOut++;
400 uint digis{0};
401 while(TrLoad(tr))
403 ch = tr->mRing[tr->mOut&TRRingMask];
404 if(!isdigit(ch)) break;
405 if(len < 64)
406 temp[len] = ch;
407 len++;
408 digis++;
409 tr->mOut++;
411 tr->mColumn += len;
412 if(digis > 0 && ch != '.' && !isalpha(ch))
414 if(len > 64)
416 TrErrorAt(tr, tr->mLine, col, "Integer is too long.");
417 return 0;
419 temp[len] = '\0';
420 *value = static_cast<int>(strtol(temp.data(), nullptr, 10));
421 if(*value < loBound || *value > hiBound)
423 TrErrorAt(tr, tr->mLine, col, "Expected a value from %d to %d.\n", loBound, hiBound);
424 return 0;
426 return 1;
429 TrErrorAt(tr, tr->mLine, col, "Expected an integer.\n");
430 return 0;
433 // Reads and validates (including bounds) a float token.
434 auto TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBound, double *value) -> int
436 uint col{tr->mColumn};
437 if(TrSkipWhitespace(tr))
439 col = tr->mColumn;
440 std::array<char,64+1> temp{};
441 uint len{0};
442 char ch{tr->mRing[tr->mOut&TRRingMask]};
443 if(ch == '+' || ch == '-')
445 temp[len] = ch;
446 len++;
447 tr->mOut++;
450 uint digis{0};
451 while(TrLoad(tr))
453 ch = tr->mRing[tr->mOut&TRRingMask];
454 if(!isdigit(ch)) break;
455 if(len < 64)
456 temp[len] = ch;
457 len++;
458 digis++;
459 tr->mOut++;
461 if(ch == '.')
463 if(len < 64)
464 temp[len] = ch;
465 len++;
466 tr->mOut++;
468 while(TrLoad(tr))
470 ch = tr->mRing[tr->mOut&TRRingMask];
471 if(!isdigit(ch)) break;
472 if(len < 64)
473 temp[len] = ch;
474 len++;
475 digis++;
476 tr->mOut++;
478 if(digis > 0)
480 if(ch == 'E' || ch == 'e')
482 if(len < 64)
483 temp[len] = ch;
484 len++;
485 digis = 0;
486 tr->mOut++;
487 if(ch == '+' || ch == '-')
489 if(len < 64)
490 temp[len] = ch;
491 len++;
492 tr->mOut++;
494 while(TrLoad(tr))
496 ch = tr->mRing[tr->mOut&TRRingMask];
497 if(!isdigit(ch)) break;
498 if(len < 64)
499 temp[len] = ch;
500 len++;
501 digis++;
502 tr->mOut++;
505 tr->mColumn += len;
506 if(digis > 0 && ch != '.' && !isalpha(ch))
508 if(len > 64)
510 TrErrorAt(tr, tr->mLine, col, "Float is too long.");
511 return 0;
513 temp[len] = '\0';
514 *value = strtod(temp.data(), nullptr);
515 if(*value < loBound || *value > hiBound)
517 TrErrorAt(tr, tr->mLine, col, "Expected a value from %f to %f.\n", loBound, hiBound);
518 return 0;
520 return 1;
523 else
524 tr->mColumn += len;
526 TrErrorAt(tr, tr->mLine, col, "Expected a float.\n");
527 return 0;
530 // Reads and validates a string token.
531 auto TrReadString(TokenReaderT *tr, const al::span<char> text) -> int
533 assert(!text.empty());
534 const size_t maxLen{text.size()-1};
536 uint col{tr->mColumn};
537 if(TrSkipWhitespace(tr))
539 col = tr->mColumn;
540 if(char ch{tr->mRing[tr->mOut&TRRingMask]}; ch == '\"')
542 tr->mOut++;
543 size_t len{0};
544 while(TrLoad(tr))
546 ch = tr->mRing[tr->mOut&TRRingMask];
547 tr->mOut++;
548 if(ch == '\"')
549 break;
550 if(ch == '\n')
552 TrErrorAt(tr, tr->mLine, col, "Unterminated string at end of line.\n");
553 return 0;
555 if(len < maxLen)
556 text[len] = ch;
557 len++;
559 if(ch != '\"')
561 tr->mColumn += static_cast<uint>(1 + len);
562 TrErrorAt(tr, tr->mLine, col, "Unterminated string at end of input.\n");
563 return 0;
565 tr->mColumn += static_cast<uint>(2 + len);
566 if(len > maxLen)
568 TrErrorAt(tr, tr->mLine, col, "String is too long.\n");
569 return 0;
571 text[len] = '\0';
572 return 1;
575 TrErrorAt(tr, tr->mLine, col, "Expected a string.\n");
576 return 0;
579 // Reads and validates the given operator.
580 auto TrReadOperator(TokenReaderT *tr, const std::string_view op) -> int
582 uint col{tr->mColumn};
583 if(TrSkipWhitespace(tr))
585 col = tr->mColumn;
586 size_t len{0};
587 while(len < op.size() && TrLoad(tr))
589 if(tr->mRing[tr->mOut&TRRingMask] != op[len])
590 break;
591 ++len;
592 tr->mOut += 1;
594 tr->mColumn += static_cast<uint>(len);
595 if(len == op.size())
596 return 1;
598 TrErrorAt(tr, tr->mLine, col, "Expected '%s' operator.\n", op);
599 return 0;
603 /*************************
604 *** File source input ***
605 *************************/
607 // Read a binary value of the specified byte order and byte size from a file,
608 // storing it as a 32-bit unsigned integer.
609 auto ReadBin4(std::istream &istream, const char *filename, const ByteOrderT order,
610 const uint bytes, uint32_t *out) -> int
612 std::array<uint8_t,4> in{};
613 istream.read(reinterpret_cast<char*>(in.data()), static_cast<int>(bytes));
614 if(istream.gcount() != bytes)
616 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
617 return 0;
619 uint32_t accum{0};
620 switch(order)
622 case BO_LITTLE:
623 for(uint i = 0;i < bytes;i++)
624 accum = (accum<<8) | in[bytes - i - 1];
625 break;
626 case BO_BIG:
627 for(uint i = 0;i < bytes;i++)
628 accum = (accum<<8) | in[i];
629 break;
630 default:
631 break;
633 *out = accum;
634 return 1;
637 // Read a binary value of the specified byte order from a file, storing it as
638 // a 64-bit unsigned integer.
639 auto ReadBin8(std::istream &istream, const char *filename, const ByteOrderT order, uint64_t *out) -> int
641 std::array<uint8_t,8> in{};
642 istream.read(reinterpret_cast<char*>(in.data()), 8);
643 if(istream.gcount() != 8)
645 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
646 return 0;
649 uint64_t accum{};
650 switch(order)
652 case BO_LITTLE:
653 for(uint i{0};i < 8;++i)
654 accum = (accum<<8) | in[8 - i - 1];
655 break;
656 case BO_BIG:
657 for(uint i{0};i < 8;++i)
658 accum = (accum<<8) | in[i];
659 break;
660 default:
661 break;
663 *out = accum;
664 return 1;
667 /* Read a binary value of the specified type, byte order, and byte size from
668 * a file, converting it to a double. For integer types, the significant
669 * bits are used to normalize the result. The sign of bits determines
670 * whether they are padded toward the MSB (negative) or LSB (positive).
671 * Floating-point types are not normalized.
673 auto ReadBinAsDouble(std::istream &istream, const char *filename, const ByteOrderT order,
674 const ElementTypeT type, const uint bytes, const int bits, double *out) -> int
676 *out = 0.0;
677 if(bytes > 4)
679 uint64_t val{};
680 if(!ReadBin8(istream, filename, order, &val))
681 return 0;
682 if(type == ET_FP)
683 *out = al::bit_cast<double>(val);
685 else
687 uint32_t val{};
688 if(!ReadBin4(istream, filename, order, bytes, &val))
689 return 0;
690 if(type == ET_FP)
691 *out = al::bit_cast<float>(val);
692 else
694 if(bits > 0)
695 val >>= (8*bytes) - (static_cast<uint>(bits));
696 else
697 val &= (0xFFFFFFFF >> (32+bits));
699 if(val&static_cast<uint>(1<<(std::abs(bits)-1)))
700 val |= (0xFFFFFFFF << std::abs(bits));
701 *out = static_cast<int32_t>(val) / static_cast<double>(1<<(std::abs(bits)-1));
704 return 1;
707 /* Read an ascii value of the specified type from a file, converting it to a
708 * double. For integer types, the significant bits are used to normalize the
709 * result. The sign of the bits should always be positive. This also skips
710 * up to one separator character before the element itself.
712 auto ReadAsciiAsDouble(TokenReaderT *tr, const char *filename, const ElementTypeT type,
713 const uint bits, double *out) -> int
715 if(TrIsOperator(tr, ","))
716 TrReadOperator(tr, ",");
717 else if(TrIsOperator(tr, ":"))
718 TrReadOperator(tr, ":");
719 else if(TrIsOperator(tr, ";"))
720 TrReadOperator(tr, ";");
721 else if(TrIsOperator(tr, "|"))
722 TrReadOperator(tr, "|");
724 if(type == ET_FP)
726 if(!TrReadFloat(tr, -std::numeric_limits<double>::infinity(),
727 std::numeric_limits<double>::infinity(), out))
729 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
730 return 0;
733 else
735 int v;
736 if(!TrReadInt(tr, -(1<<(bits-1)), (1<<(bits-1))-1, &v))
738 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
739 return 0;
741 *out = v / static_cast<double>((1<<(bits-1))-1);
743 return 1;
746 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
747 // the source parameters and data set metrics.
748 auto ReadWaveFormat(std::istream &istream, const ByteOrderT order, const uint hrirRate,
749 SourceRefT *src) -> int
751 uint32_t fourCC, chunkSize;
752 uint32_t format, channels, rate, dummy, block, size, bits;
754 chunkSize = 0;
755 do {
756 if(chunkSize > 0)
757 istream.seekg(static_cast<int>(chunkSize), std::ios::cur);
758 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
759 || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize))
760 return 0;
761 } while(fourCC != FOURCC_FMT);
762 if(!ReadBin4(istream, src->mPath.data(), order, 2, &format)
763 || !ReadBin4(istream, src->mPath.data(), order, 2, &channels)
764 || !ReadBin4(istream, src->mPath.data(), order, 4, &rate)
765 || !ReadBin4(istream, src->mPath.data(), order, 4, &dummy)
766 || !ReadBin4(istream, src->mPath.data(), order, 2, &block))
767 return 0;
768 block /= channels;
769 if(chunkSize > 14)
771 if(!ReadBin4(istream, src->mPath.data(), order, 2, &size))
772 return 0;
773 size /= 8;
774 if(block > size)
775 size = block;
777 else
778 size = block;
779 if(format == WAVE_FORMAT_EXTENSIBLE)
781 istream.seekg(2, std::ios::cur);
782 if(!ReadBin4(istream, src->mPath.data(), order, 2, &bits))
783 return 0;
784 if(bits == 0)
785 bits = 8 * size;
786 istream.seekg(4, std::ios::cur);
787 if(!ReadBin4(istream, src->mPath.data(), order, 2, &format))
788 return 0;
789 istream.seekg(static_cast<int>(chunkSize - 26), std::ios::cur);
791 else
793 bits = 8 * size;
794 if(chunkSize > 14)
795 istream.seekg(static_cast<int>(chunkSize - 16), std::ios::cur);
796 else
797 istream.seekg(static_cast<int>(chunkSize - 14), std::ios::cur);
799 if(format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_IEEE_FLOAT)
801 fprintf(stderr, "\nError: Unsupported WAVE format in file '%s'.\n", src->mPath.data());
802 return 0;
804 if(src->mChannel >= channels)
806 fprintf(stderr, "\nError: Missing source channel in WAVE file '%s'.\n", src->mPath.data());
807 return 0;
809 if(rate != hrirRate)
811 fprintf(stderr, "\nError: Mismatched source sample rate in WAVE file '%s'.\n",
812 src->mPath.data());
813 return 0;
815 if(format == WAVE_FORMAT_PCM)
817 if(size < 2 || size > 4)
819 fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n",
820 src->mPath.data());
821 return 0;
823 if(bits < 16 || bits > (8*size))
825 fprintf(stderr, "\nError: Bad significant bits in WAVE file '%s'.\n",
826 src->mPath.data());
827 return 0;
829 src->mType = ET_INT;
831 else
833 if(size != 4 && size != 8)
835 fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n",
836 src->mPath.data());
837 return 0;
839 src->mType = ET_FP;
841 src->mSize = size;
842 src->mBits = static_cast<int>(bits);
843 src->mSkip = channels;
844 return 1;
847 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
848 auto ReadWaveData(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
849 const al::span<double> hrir) -> int
851 auto pre = static_cast<int>(src->mSize * src->mChannel);
852 auto post = static_cast<int>(src->mSize * (src->mSkip - src->mChannel - 1));
853 auto skip = 0;
854 for(size_t i{0};i < hrir.size();++i)
856 skip += pre;
857 if(skip > 0)
858 istream.seekg(skip, std::ios::cur);
859 if(!ReadBinAsDouble(istream, src->mPath.data(), order, src->mType, src->mSize, src->mBits,
860 &hrir[i]))
861 return 0;
862 skip = post;
864 if(skip > 0)
865 istream.seekg(skip, std::ios::cur);
866 return 1;
869 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
870 // doubles.
871 auto ReadWaveList(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
872 const al::span<double> hrir) -> int
874 uint32_t fourCC, chunkSize, listSize, count;
875 uint block, skip, offset, i;
876 double lastSample;
878 for(;;)
880 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
881 || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize))
882 return 0;
884 if(fourCC == FOURCC_DATA)
886 block = src->mSize * src->mSkip;
887 count = chunkSize / block;
888 if(count < (src->mOffset + hrir.size()))
890 fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath.data());
891 return 0;
893 using off_type = std::istream::off_type;
894 istream.seekg(off_type(src->mOffset) * off_type(block), std::ios::cur);
895 if(!ReadWaveData(istream, src, order, hrir))
896 return 0;
897 return 1;
899 if(fourCC == FOURCC_LIST)
901 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC))
902 return 0;
903 chunkSize -= 4;
904 if(fourCC == FOURCC_WAVL)
905 break;
907 if(chunkSize > 0)
908 istream.seekg(static_cast<long>(chunkSize), std::ios::cur);
910 listSize = chunkSize;
911 block = src->mSize * src->mSkip;
912 skip = src->mOffset;
913 offset = 0;
914 lastSample = 0.0;
915 while(offset < hrir.size() && listSize > 8)
917 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
918 || !ReadBin4(istream, src->mPath.data(), order, 4, &chunkSize))
919 return 0;
920 listSize -= 8 + chunkSize;
921 if(fourCC == FOURCC_DATA)
923 count = chunkSize / block;
924 if(count > skip)
926 using off_type = std::istream::off_type;
927 istream.seekg(off_type(skip) * off_type(block), std::ios::cur);
928 chunkSize -= skip * block;
929 count -= skip;
930 skip = 0;
931 if(count > (hrir.size() - offset))
932 count = static_cast<uint>(hrir.size() - offset);
933 if(!ReadWaveData(istream, src, order, hrir.subspan(offset, count)))
934 return 0;
935 chunkSize -= count * block;
936 offset += count;
937 lastSample = hrir[offset - 1];
939 else
941 skip -= count;
942 count = 0;
945 else if(fourCC == FOURCC_SLNT)
947 if(!ReadBin4(istream, src->mPath.data(), order, 4, &count))
948 return 0;
949 chunkSize -= 4;
950 if(count > skip)
952 count -= skip;
953 skip = 0;
954 if(count > (hrir.size() - offset))
955 count = static_cast<uint>(hrir.size() - offset);
956 for(i = 0; i < count; i ++)
957 hrir[offset + i] = lastSample;
958 offset += count;
960 else
962 skip -= count;
963 count = 0;
966 if(chunkSize > 0)
967 istream.seekg(static_cast<long>(chunkSize), std::ios::cur);
969 if(offset < hrir.size())
971 fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath.data());
972 return 0;
974 return 1;
977 // Load a source HRIR from an ASCII text file containing a list of elements
978 // separated by whitespace or common list operators (',', ';', ':', '|').
979 auto LoadAsciiSource(std::istream &istream, const SourceRefT *src, const al::span<double> hrir) -> int
981 TokenReaderT tr{istream};
983 TrSetup({}, {}, &tr);
984 for(uint i{0};i < src->mOffset;++i)
986 double dummy{};
987 if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, static_cast<uint>(src->mBits),
988 &dummy))
989 return 0;
991 for(size_t i{0};i < hrir.size();++i)
993 if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType, static_cast<uint>(src->mBits),
994 &hrir[i]))
995 return 0;
996 for(uint j{0};j < src->mSkip;++j)
998 double dummy{};
999 if(!ReadAsciiAsDouble(&tr, src->mPath.data(), src->mType,
1000 static_cast<uint>(src->mBits), &dummy))
1001 return 0;
1004 return 1;
1007 // Load a source HRIR from a binary file.
1008 auto LoadBinarySource(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
1009 const al::span<double> hrir) -> int
1011 istream.seekg(static_cast<long>(src->mOffset), std::ios::beg);
1012 for(size_t i{0};i < hrir.size();++i)
1014 if(!ReadBinAsDouble(istream, src->mPath.data(), order, src->mType, src->mSize, src->mBits,
1015 &hrir[i]))
1016 return 0;
1017 if(src->mSkip > 0)
1018 istream.seekg(static_cast<long>(src->mSkip), std::ios::cur);
1020 return 1;
1023 // Load a source HRIR from a RIFF/RIFX WAVE file.
1024 auto LoadWaveSource(std::istream &istream, SourceRefT *src, const uint hrirRate,
1025 const al::span<double> hrir) -> int
1027 uint32_t fourCC, dummy;
1028 ByteOrderT order;
1030 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC)
1031 || !ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &dummy))
1032 return 0;
1033 if(fourCC == FOURCC_RIFF)
1034 order = BO_LITTLE;
1035 else if(fourCC == FOURCC_RIFX)
1036 order = BO_BIG;
1037 else
1039 fprintf(stderr, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src->mPath.data());
1040 return 0;
1043 if(!ReadBin4(istream, src->mPath.data(), BO_LITTLE, 4, &fourCC))
1044 return 0;
1045 if(fourCC != FOURCC_WAVE)
1047 fprintf(stderr, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src->mPath.data());
1048 return 0;
1050 if(!ReadWaveFormat(istream, order, hrirRate, src))
1051 return 0;
1052 if(!ReadWaveList(istream, src, order, hrir))
1053 return 0;
1054 return 1;
1058 struct SofaEasyDeleter {
1059 void operator()(gsl::owner<MYSOFA_EASY*> sofa)
1061 if(sofa->neighborhood) mysofa_neighborhood_free(sofa->neighborhood);
1062 if(sofa->lookup) mysofa_lookup_free(sofa->lookup);
1063 if(sofa->hrtf) mysofa_free(sofa->hrtf);
1064 delete sofa;
1067 using SofaEasyPtr = std::unique_ptr<MYSOFA_EASY,SofaEasyDeleter>;
1069 struct SofaCacheEntry {
1070 std::string mName;
1071 uint mSampleRate{};
1072 SofaEasyPtr mSofa;
1074 std::vector<SofaCacheEntry> gSofaCache;
1076 // Load a Spatially Oriented Format for Accoustics (SOFA) file.
1077 auto LoadSofaFile(SourceRefT *src, const uint hrirRate, const uint n) -> MYSOFA_EASY*
1079 const std::string_view srcname{src->mPath.data()};
1080 auto iter = std::find_if(gSofaCache.begin(), gSofaCache.end(),
1081 [srcname,hrirRate](SofaCacheEntry &entry) -> bool
1082 { return entry.mName == srcname && entry.mSampleRate == hrirRate; });
1083 if(iter != gSofaCache.end()) return iter->mSofa.get();
1085 SofaEasyPtr sofa{new(std::nothrow) MYSOFA_EASY{}};
1086 if(!sofa)
1088 fprintf(stderr, "\nError: Out of memory.\n");
1089 return nullptr;
1091 sofa->lookup = nullptr;
1092 sofa->neighborhood = nullptr;
1094 int err;
1095 sofa->hrtf = mysofa_load(src->mPath.data(), &err);
1096 if(!sofa->hrtf)
1098 fprintf(stderr, "\nError: Could not load source file '%s': %s (%d).\n",
1099 src->mPath.data(), SofaErrorStr(err), err);
1100 return nullptr;
1102 /* NOTE: Some valid SOFA files are failing this check. */
1103 err = mysofa_check(sofa->hrtf);
1104 if(err != MYSOFA_OK)
1105 fprintf(stderr, "\nWarning: Supposedly malformed source file '%s': %s (%d).\n",
1106 src->mPath.data(), SofaErrorStr(err), err);
1107 if((src->mOffset + n) > sofa->hrtf->N)
1109 fprintf(stderr, "\nError: Not enough samples in SOFA file '%s'.\n", src->mPath.data());
1110 return nullptr;
1112 if(src->mChannel >= sofa->hrtf->R)
1114 fprintf(stderr, "\nError: Missing source receiver in SOFA file '%s'.\n",src->mPath.data());
1115 return nullptr;
1117 mysofa_tocartesian(sofa->hrtf);
1118 sofa->lookup = mysofa_lookup_init(sofa->hrtf);
1119 if(sofa->lookup == nullptr)
1121 fprintf(stderr, "\nError: Out of memory.\n");
1122 return nullptr;
1124 gSofaCache.emplace_back(SofaCacheEntry{std::string{srcname}, hrirRate, std::move(sofa)});
1125 return gSofaCache.back().mSofa.get();
1128 // Copies the HRIR data from a particular SOFA measurement.
1129 void ExtractSofaHrir(const MYSOFA_HRTF *hrtf, const size_t index, const size_t channel,
1130 const size_t offset, const al::span<double> hrir)
1132 const auto irValues = al::span{hrtf->DataIR.values, hrtf->DataIR.elements}
1133 .subspan((index*hrtf->R + channel)*hrtf->N + offset);
1134 std::copy_n(irValues.cbegin(), hrir.size(), hrir.begin());
1137 // Load a source HRIR from a Spatially Oriented Format for Accoustics (SOFA)
1138 // file.
1139 auto LoadSofaSource(SourceRefT *src, const uint hrirRate, const al::span<double> hrir) -> int
1141 MYSOFA_EASY *sofa{LoadSofaFile(src, hrirRate, static_cast<uint>(hrir.size()))};
1142 if(sofa == nullptr) return 0;
1144 /* NOTE: At some point it may be beneficial or necessary to consider the
1145 various coordinate systems, listener/source orientations, and
1146 directional vectors defined in the SOFA file.
1148 std::array target{
1149 static_cast<float>(src->mAzimuth),
1150 static_cast<float>(src->mElevation),
1151 static_cast<float>(src->mRadius)
1153 mysofa_s2c(target.data());
1155 int nearest{mysofa_lookup(sofa->lookup, target.data())};
1156 if(nearest < 0)
1158 fprintf(stderr, "\nError: Lookup failed in source file '%s'.\n", src->mPath.data());
1159 return 0;
1162 al::span<float,3> coords = al::span{sofa->hrtf->SourcePosition.values, sofa->hrtf->M*3_uz}
1163 .subspan(static_cast<uint>(nearest)*3_uz).first<3>();
1164 if(std::abs(coords[0] - target[0]) > 0.001 || std::abs(coords[1] - target[1]) > 0.001
1165 || std::abs(coords[2] - target[2]) > 0.001)
1167 fprintf(stderr, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n",
1168 src->mRadius, src->mElevation, src->mAzimuth, src->mPath.data());
1169 target[0] = coords[0];
1170 target[1] = coords[1];
1171 target[2] = coords[2];
1172 mysofa_c2s(target.data());
1173 fprintf(stderr, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target[2],
1174 target[1], target[0]);
1175 return 0;
1178 ExtractSofaHrir(sofa->hrtf, static_cast<uint>(nearest), src->mChannel, src->mOffset, hrir);
1180 return 1;
1183 // Load a source HRIR from a supported file type.
1184 auto LoadSource(SourceRefT *src, const uint hrirRate, const al::span<double> hrir) -> int
1186 std::unique_ptr<std::istream> istream;
1187 if(src->mFormat != SF_SOFA)
1189 if(src->mFormat == SF_ASCII)
1190 istream = std::make_unique<std::ifstream>(std::filesystem::u8path(src->mPath.data()));
1191 else
1192 istream = std::make_unique<std::ifstream>(std::filesystem::u8path(src->mPath.data()),
1193 std::ios::binary);
1194 if(!istream->good())
1196 fprintf(stderr, "\nError: Could not open source file '%s'.\n", src->mPath.data());
1197 return 0;
1201 switch(src->mFormat)
1203 case SF_ASCII: return LoadAsciiSource(*istream, src, hrir);
1204 case SF_BIN_LE: return LoadBinarySource(*istream, src, BO_LITTLE, hrir);
1205 case SF_BIN_BE: return LoadBinarySource(*istream, src, BO_BIG, hrir);
1206 case SF_WAVE: return LoadWaveSource(*istream, src, hrirRate, hrir);
1207 case SF_SOFA: return LoadSofaSource(src, hrirRate, hrir);
1208 case SF_NONE: break;
1210 return 0;
1214 // Match the channel type from a given identifier.
1215 auto MatchChannelType(const std::string_view ident) -> ChannelTypeT
1217 if(al::case_compare(ident, "mono"sv) == 0)
1218 return CT_MONO;
1219 if(al::case_compare(ident, "stereo"sv) == 0)
1220 return CT_STEREO;
1221 return CT_NONE;
1225 // Process the data set definition to read and validate the data set metrics.
1226 auto ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint truncSize,
1227 const ChannelModeT chanMode, HrirDataT *hData) -> int
1229 int hasRate = 0, hasType = 0, hasPoints = 0, hasRadius = 0;
1230 int hasDistance = 0, hasAzimuths = 0;
1231 uint line, col;
1232 double fpVal;
1233 uint points;
1234 int intVal;
1235 std::array<double,MAX_FD_COUNT> distances{};
1236 uint fdCount = 0;
1237 std::array<uint,MAX_FD_COUNT> evCounts{};
1238 auto azCounts = std::vector<std::array<uint,MAX_EV_COUNT>>(MAX_FD_COUNT);
1239 for(auto &azs : azCounts) azs.fill(0u);
1241 TrIndication(tr, &line, &col);
1242 while(TrIsIdent(tr))
1244 TrIndication(tr, &line, &col);
1245 const auto ident = TrReadIdent(tr);
1246 if(ident.empty())
1247 return 0;
1248 if(al::case_compare(ident, "rate"sv) == 0)
1250 if(hasRate)
1252 TrErrorAt(tr, line, col, "Redefinition of 'rate'.\n");
1253 return 0;
1255 if(!TrReadOperator(tr, "="))
1256 return 0;
1257 if(!TrReadInt(tr, MIN_RATE, MAX_RATE, &intVal))
1258 return 0;
1259 hData->mIrRate = static_cast<uint>(intVal);
1260 hasRate = 1;
1262 else if(al::case_compare(ident, "type"sv) == 0)
1264 if(hasType)
1266 TrErrorAt(tr, line, col, "Redefinition of 'type'.\n");
1267 return 0;
1269 if(!TrReadOperator(tr, "="))
1270 return 0;
1272 const auto type = TrReadIdent(tr);
1273 if(type.empty())
1274 return 0;
1275 hData->mChannelType = MatchChannelType(type);
1276 if(hData->mChannelType == CT_NONE)
1278 TrErrorAt(tr, line, col, "Expected a channel type.\n");
1279 return 0;
1281 if(hData->mChannelType == CT_STEREO)
1283 if(chanMode == CM_ForceMono)
1284 hData->mChannelType = CT_MONO;
1286 hasType = 1;
1288 else if(al::case_compare(ident, "points"sv) == 0)
1290 if(hasPoints)
1292 TrErrorAt(tr, line, col, "Redefinition of 'points'.\n");
1293 return 0;
1295 if(!TrReadOperator(tr, "="))
1296 return 0;
1297 TrIndication(tr, &line, &col);
1298 if(!TrReadInt(tr, MIN_POINTS, MAX_POINTS, &intVal))
1299 return 0;
1300 points = static_cast<uint>(intVal);
1301 if(fftSize > 0 && points > fftSize)
1303 TrErrorAt(tr, line, col, "Value exceeds the overridden FFT size.\n");
1304 return 0;
1306 if(points < truncSize)
1308 TrErrorAt(tr, line, col, "Value is below the truncation size.\n");
1309 return 0;
1311 hData->mIrPoints = points;
1312 hData->mFftSize = fftSize;
1313 hData->mIrSize = 1 + (fftSize / 2);
1314 if(points > hData->mIrSize)
1315 hData->mIrSize = points;
1316 hasPoints = 1;
1318 else if(al::case_compare(ident, "radius"sv) == 0)
1320 if(hasRadius)
1322 TrErrorAt(tr, line, col, "Redefinition of 'radius'.\n");
1323 return 0;
1325 if(!TrReadOperator(tr, "="))
1326 return 0;
1327 if(!TrReadFloat(tr, MinRadius, MaxRadius, &fpVal))
1328 return 0;
1329 hData->mRadius = fpVal;
1330 hasRadius = 1;
1332 else if(al::case_compare(ident, "distance"sv) == 0)
1334 auto count = uint{0};
1336 if(hasDistance)
1338 TrErrorAt(tr, line, col, "Redefinition of 'distance'.\n");
1339 return 0;
1341 if(!TrReadOperator(tr, "="))
1342 return 0;
1344 for(;;)
1346 if(!TrReadFloat(tr, MIN_DISTANCE, MAX_DISTANCE, &fpVal))
1347 return 0;
1348 if(count > 0 && fpVal <= distances[count - 1])
1350 TrError(tr, "Distances are not ascending.\n");
1351 return 0;
1353 distances[count++] = fpVal;
1354 if(!TrIsOperator(tr, ","))
1355 break;
1356 if(count >= MAX_FD_COUNT)
1358 TrError(tr, "Exceeded the maximum of %d fields.\n", MAX_FD_COUNT);
1359 return 0;
1361 TrReadOperator(tr, ",");
1363 if(fdCount != 0 && count != fdCount)
1365 TrError(tr, "Did not match the specified number of %d fields.\n", fdCount);
1366 return 0;
1368 fdCount = count;
1369 hasDistance = 1;
1371 else if(al::case_compare(ident, "azimuths"sv) == 0)
1373 auto count = uint{0};
1375 if(hasAzimuths)
1377 TrErrorAt(tr, line, col, "Redefinition of 'azimuths'.\n");
1378 return 0;
1380 if(!TrReadOperator(tr, "="))
1381 return 0;
1383 evCounts[0] = 0;
1384 for(;;)
1386 if(!TrReadInt(tr, MIN_AZ_COUNT, MAX_AZ_COUNT, &intVal))
1387 return 0;
1388 azCounts[count][evCounts[count]++] = static_cast<uint>(intVal);
1389 if(TrIsOperator(tr, ","))
1391 if(evCounts[count] >= MAX_EV_COUNT)
1393 TrError(tr, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT);
1394 return 0;
1396 TrReadOperator(tr, ",");
1398 else
1400 if(evCounts[count] < MIN_EV_COUNT)
1402 TrErrorAt(tr, line, col, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT);
1403 return 0;
1405 if(azCounts[count][0] != 1 || azCounts[count][evCounts[count] - 1] != 1)
1407 TrError(tr, "Poles are not singular for field %d.\n", count - 1);
1408 return 0;
1410 count++;
1411 if(!TrIsOperator(tr, ";"))
1412 break;
1414 if(count >= MAX_FD_COUNT)
1416 TrError(tr, "Exceeded the maximum number of %d fields.\n", MAX_FD_COUNT);
1417 return 0;
1419 evCounts[count] = 0;
1420 TrReadOperator(tr, ";");
1423 if(fdCount != 0 && count != fdCount)
1425 TrError(tr, "Did not match the specified number of %d fields.\n", fdCount);
1426 return 0;
1428 fdCount = count;
1429 hasAzimuths = 1;
1431 else
1433 TrErrorAt(tr, line, col, "Expected a metric name.\n");
1434 return 0;
1436 TrSkipWhitespace(tr);
1438 if(!(hasRate && hasPoints && hasRadius && hasDistance && hasAzimuths))
1440 TrErrorAt(tr, line, col, "Expected a metric name.\n");
1441 return 0;
1443 if(distances[0] < hData->mRadius)
1445 TrError(tr, "Distance cannot start below head radius.\n");
1446 return 0;
1448 if(hData->mChannelType == CT_NONE)
1449 hData->mChannelType = CT_MONO;
1450 const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>();
1451 if(!PrepareHrirData(al::span{distances}.first(fdCount), evCounts, azs, hData))
1453 fprintf(stderr, "Error: Out of memory.\n");
1454 exit(-1);
1456 return 1;
1459 // Parse an index triplet from the data set definition.
1460 auto ReadIndexTriplet(TokenReaderT *tr, const HrirDataT *hData, uint *fi, uint *ei, uint *ai)->int
1462 int intVal;
1464 if(hData->mFds.size() > 1)
1466 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds.size()-1), &intVal))
1467 return 0;
1468 *fi = static_cast<uint>(intVal);
1469 if(!TrReadOperator(tr, ","))
1470 return 0;
1472 else
1474 *fi = 0;
1476 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds[*fi].mEvs.size()-1), &intVal))
1477 return 0;
1478 *ei = static_cast<uint>(intVal);
1479 if(!TrReadOperator(tr, ","))
1480 return 0;
1481 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds[*fi].mEvs[*ei].mAzs.size()-1), &intVal))
1482 return 0;
1483 *ai = static_cast<uint>(intVal);
1484 return 1;
1487 // Match the source format from a given identifier.
1488 auto MatchSourceFormat(const std::string_view ident) -> SourceFormatT
1490 if(al::case_compare(ident, "ascii"sv) == 0)
1491 return SF_ASCII;
1492 if(al::case_compare(ident, "bin_le"sv) == 0)
1493 return SF_BIN_LE;
1494 if(al::case_compare(ident, "bin_be"sv) == 0)
1495 return SF_BIN_BE;
1496 if(al::case_compare(ident, "wave"sv) == 0)
1497 return SF_WAVE;
1498 if(al::case_compare(ident, "sofa"sv) == 0)
1499 return SF_SOFA;
1500 return SF_NONE;
1503 // Match the source element type from a given identifier.
1504 auto MatchElementType(const std::string_view ident) -> ElementTypeT
1506 if(al::case_compare(ident, "int"sv) == 0)
1507 return ET_INT;
1508 if(al::case_compare(ident, "fp"sv) == 0)
1509 return ET_FP;
1510 return ET_NONE;
1513 // Parse and validate a source reference from the data set definition.
1514 auto ReadSourceRef(TokenReaderT *tr, SourceRefT *src) -> int
1516 uint line, col;
1517 double fpVal;
1518 int intVal;
1520 TrIndication(tr, &line, &col);
1521 auto ident = TrReadIdent(tr);
1522 if(ident.empty())
1523 return 0;
1524 src->mFormat = MatchSourceFormat(ident);
1525 if(src->mFormat == SF_NONE)
1527 TrErrorAt(tr, line, col, "Expected a source format.\n");
1528 return 0;
1530 if(!TrReadOperator(tr, "("))
1531 return 0;
1532 if(src->mFormat == SF_SOFA)
1534 if(!TrReadFloat(tr, MIN_DISTANCE, MAX_DISTANCE, &fpVal))
1535 return 0;
1536 src->mRadius = fpVal;
1537 if(!TrReadOperator(tr, ","))
1538 return 0;
1539 if(!TrReadFloat(tr, -90.0, 90.0, &fpVal))
1540 return 0;
1541 src->mElevation = fpVal;
1542 if(!TrReadOperator(tr, ","))
1543 return 0;
1544 if(!TrReadFloat(tr, -360.0, 360.0, &fpVal))
1545 return 0;
1546 src->mAzimuth = fpVal;
1547 if(!TrReadOperator(tr, ":"))
1548 return 0;
1549 if(!TrReadInt(tr, 0, MaxWaveChannels, &intVal))
1550 return 0;
1551 src->mType = ET_NONE;
1552 src->mSize = 0;
1553 src->mBits = 0;
1554 src->mChannel = static_cast<uint>(intVal);
1555 src->mSkip = 0;
1557 else if(src->mFormat == SF_WAVE)
1559 if(!TrReadInt(tr, 0, MaxWaveChannels, &intVal))
1560 return 0;
1561 src->mType = ET_NONE;
1562 src->mSize = 0;
1563 src->mBits = 0;
1564 src->mChannel = static_cast<uint>(intVal);
1565 src->mSkip = 0;
1567 else
1569 TrIndication(tr, &line, &col);
1570 ident = TrReadIdent(tr);
1571 if(ident.empty())
1572 return 0;
1573 src->mType = MatchElementType(ident);
1574 if(src->mType == ET_NONE)
1576 TrErrorAt(tr, line, col, "Expected a source element type.\n");
1577 return 0;
1579 if(src->mFormat == SF_BIN_LE || src->mFormat == SF_BIN_BE)
1581 if(!TrReadOperator(tr, ","))
1582 return 0;
1583 if(src->mType == ET_INT)
1585 if(!TrReadInt(tr, MinBinSize, MaxBinSize, &intVal))
1586 return 0;
1587 src->mSize = static_cast<uint>(intVal);
1588 if(!TrIsOperator(tr, ","))
1589 src->mBits = static_cast<int>(8*src->mSize);
1590 else
1592 TrReadOperator(tr, ",");
1593 TrIndication(tr, &line, &col);
1594 if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal))
1595 return 0;
1596 if(std::abs(intVal) < int{MinBinSize}*8 || static_cast<uint>(std::abs(intVal)) > (8*src->mSize))
1598 TrErrorAt(tr, line, col, "Expected a value of (+/-) %d to %d.\n", MinBinSize*8, 8*src->mSize);
1599 return 0;
1601 src->mBits = intVal;
1604 else
1606 TrIndication(tr, &line, &col);
1607 if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal))
1608 return 0;
1609 if(intVal != 4 && intVal != 8)
1611 TrErrorAt(tr, line, col, "Expected a value of 4 or 8.\n");
1612 return 0;
1614 src->mSize = static_cast<uint>(intVal);
1615 src->mBits = 0;
1618 else if(src->mFormat == SF_ASCII && src->mType == ET_INT)
1620 if(!TrReadOperator(tr, ","))
1621 return 0;
1622 if(!TrReadInt(tr, MinASCIIBits, MaxASCIIBits, &intVal))
1623 return 0;
1624 src->mSize = 0;
1625 src->mBits = intVal;
1627 else
1629 src->mSize = 0;
1630 src->mBits = 0;
1633 if(!TrIsOperator(tr, ";"))
1634 src->mSkip = 0;
1635 else
1637 TrReadOperator(tr, ";");
1638 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1639 return 0;
1640 src->mSkip = static_cast<uint>(intVal);
1643 if(!TrReadOperator(tr, ")"))
1644 return 0;
1645 if(TrIsOperator(tr, "@"))
1647 TrReadOperator(tr, "@");
1648 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1649 return 0;
1650 src->mOffset = static_cast<uint>(intVal);
1652 else
1653 src->mOffset = 0;
1654 if(!TrReadOperator(tr, ":"))
1655 return 0;
1656 if(!TrReadString(tr, src->mPath))
1657 return 0;
1658 return 1;
1661 // Parse and validate a SOFA source reference from the data set definition.
1662 auto ReadSofaRef(TokenReaderT *tr, SourceRefT *src) -> int
1664 uint line, col;
1665 int intVal;
1667 TrIndication(tr, &line, &col);
1668 const auto ident = TrReadIdent(tr);
1669 if(ident.empty())
1670 return 0;
1671 src->mFormat = MatchSourceFormat(ident);
1672 if(src->mFormat != SF_SOFA)
1674 TrErrorAt(tr, line, col, "Expected the SOFA source format.\n");
1675 return 0;
1678 src->mType = ET_NONE;
1679 src->mSize = 0;
1680 src->mBits = 0;
1681 src->mChannel = 0;
1682 src->mSkip = 0;
1684 if(TrIsOperator(tr, "@"))
1686 TrReadOperator(tr, "@");
1687 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1688 return 0;
1689 src->mOffset = static_cast<uint>(intVal);
1691 else
1692 src->mOffset = 0;
1693 if(!TrReadOperator(tr, ":"))
1694 return 0;
1695 if(!TrReadString(tr, src->mPath))
1696 return 0;
1697 return 1;
1700 // Match the target ear (index) from a given identifier.
1701 auto MatchTargetEar(const std::string_view ident) -> std::optional<uint8_t>
1703 if(al::case_compare(ident, "left"sv) == 0)
1704 return 0u;
1705 if(al::case_compare(ident, "right"sv) == 0)
1706 return 1u;
1707 return std::nullopt;
1710 // Calculate the onset time of an HRIR and average it with any existing
1711 // timing for its field, elevation, azimuth, and ear.
1712 constexpr int OnsetRateMultiple{10};
1713 auto AverageHrirOnset(PPhaseResampler &rs, al::span<double> upsampled, const uint rate,
1714 const al::span<const double> hrir, const double f, const double onset) -> double
1716 rs.process(hrir, upsampled);
1718 auto abs_lt = [](const double lhs, const double rhs) -> bool
1719 { return std::abs(lhs) < std::abs(rhs); };
1720 auto iter = std::max_element(upsampled.cbegin(), upsampled.cend(), abs_lt);
1721 return Lerp(onset, static_cast<double>(std::distance(upsampled.cbegin(), iter))/(10*rate), f);
1724 // Calculate the magnitude response of an HRIR and average it with any
1725 // existing responses for its field, elevation, azimuth, and ear.
1726 void AverageHrirMagnitude(const uint fftSize, const al::span<const double> hrir, const double f,
1727 const al::span<double> mag)
1729 const uint m{1 + (fftSize/2)};
1730 std::vector<complex_d> h(fftSize);
1731 std::vector<double> r(m);
1733 auto hiter = std::copy(hrir.cbegin(), hrir.cend(), h.begin());
1734 std::fill(hiter, h.end(), 0.0);
1735 forward_fft(h);
1736 MagnitudeResponse(h, r);
1737 for(uint i{0};i < m;++i)
1738 mag[i] = Lerp(mag[i], r[i], f);
1741 // Process the list of sources in the data set definition.
1742 auto ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate) -> int
1744 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
1745 hData->mHrirsBase.resize(size_t{channels} * hData->mIrCount * hData->mIrSize);
1746 const auto hrirs = al::span<double>{hData->mHrirsBase};
1747 auto hrir = std::vector<double>(hData->mIrSize);
1748 uint line, col, fi, ei, ai;
1750 std::vector<double> onsetSamples(size_t{OnsetRateMultiple} * hData->mIrPoints);
1751 PPhaseResampler onsetResampler;
1752 onsetResampler.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate);
1754 std::optional<PPhaseResampler> resampler;
1755 if(outRate && outRate != hData->mIrRate)
1756 resampler.emplace().init(hData->mIrRate, outRate);
1757 const double rateScale{outRate ? static_cast<double>(outRate) / hData->mIrRate : 1.0};
1758 const uint irPoints{outRate
1759 ? std::min(static_cast<uint>(std::ceil(hData->mIrPoints*rateScale)), hData->mIrPoints)
1760 : hData->mIrPoints};
1762 printf("Loading sources...");
1763 fflush(stdout);
1764 int count{0};
1765 while(TrIsOperator(tr, "["))
1767 std::array factor{1.0, 1.0};
1769 TrIndication(tr, &line, &col);
1770 TrReadOperator(tr, "[");
1772 if(TrIsOperator(tr, "*"))
1774 TrReadOperator(tr, "*");
1775 if(!TrReadOperator(tr, "]") || !TrReadOperator(tr, "="))
1776 return 0;
1778 TrIndication(tr, &line, &col);
1779 SourceRefT src{};
1780 if(!ReadSofaRef(tr, &src))
1781 return 0;
1783 if(hData->mChannelType == CT_STEREO)
1785 const auto type = TrReadIdent(tr);
1786 if(type.empty())
1787 return 0;
1789 switch(MatchChannelType(type))
1791 case CT_NONE:
1792 TrErrorAt(tr, line, col, "Expected a channel type.\n");
1793 return 0;
1794 case CT_MONO:
1795 src.mChannel = 0;
1796 break;
1797 case CT_STEREO:
1798 src.mChannel = 1;
1799 break;
1802 else
1804 const auto type = TrReadIdent(tr);
1805 if(type.empty())
1806 return 0;
1808 if(MatchChannelType(type) != CT_MONO)
1810 TrErrorAt(tr, line, col, "Expected a mono channel type.\n");
1811 return 0;
1813 src.mChannel = 0;
1816 MYSOFA_EASY *sofa{LoadSofaFile(&src, hData->mIrRate, hData->mIrPoints)};
1817 if(!sofa) return 0;
1819 const auto srcPosValues = al::span{sofa->hrtf->SourcePosition.values,
1820 sofa->hrtf->M*3_uz};
1821 for(uint si{0};si < sofa->hrtf->M;++si)
1823 printf("\rLoading sources... %d of %d", si+1, sofa->hrtf->M);
1824 fflush(stdout);
1826 std::array aer{srcPosValues[3_uz*si], srcPosValues[3_uz*si + 1],
1827 srcPosValues[3_uz*si + 2]};
1828 mysofa_c2s(aer.data());
1830 if(std::fabs(aer[1]) >= 89.999f)
1831 aer[0] = 0.0f;
1832 else
1833 aer[0] = std::fmod(360.0f - aer[0], 360.0f);
1835 auto field = std::find_if(hData->mFds.cbegin(), hData->mFds.cend(),
1836 [&aer](const HrirFdT &fld) -> bool
1837 { return (std::abs(aer[2] - fld.mDistance) < 0.001); });
1838 if(field == hData->mFds.cend())
1839 continue;
1840 fi = static_cast<uint>(std::distance(hData->mFds.cbegin(), field));
1842 const double evscale{180.0 / static_cast<double>(field->mEvs.size()-1)};
1843 double ef{(90.0 + aer[1]) / evscale};
1844 ei = static_cast<uint>(std::round(ef));
1845 ef = (ef - ei) * evscale;
1846 if(std::abs(ef) >= 0.1)
1847 continue;
1849 const double azscale{360.0 / static_cast<double>(field->mEvs[ei].mAzs.size())};
1850 double af{aer[0] / azscale};
1851 ai = static_cast<uint>(std::round(af));
1852 af = (af - ai) * azscale;
1853 ai %= static_cast<uint>(field->mEvs[ei].mAzs.size());
1854 if(std::abs(af) >= 0.1)
1855 continue;
1857 HrirAzT *azd = &field->mEvs[ei].mAzs[ai];
1858 if(!azd->mIrs[0].empty())
1860 TrErrorAt(tr, line, col, "Redefinition of source [ %d, %d, %d ].\n", fi, ei, ai);
1861 return 0;
1864 const auto hrirPoints = al::span{hrir}.first(hData->mIrPoints);
1865 ExtractSofaHrir(sofa->hrtf, si, 0, src.mOffset, hrirPoints);
1866 azd->mIrs[0] = hrirs.subspan(size_t{hData->mIrSize}*azd->mIndex, hData->mIrSize);
1867 azd->mDelays[0] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate,
1868 hrirPoints, 1.0, azd->mDelays[0]);
1869 if(resampler)
1870 resampler->process(hrirPoints, hrir);
1871 AverageHrirMagnitude(hData->mFftSize, al::span{hrir}.first(irPoints), 1.0,
1872 azd->mIrs[0]);
1874 if(src.mChannel == 1)
1876 ExtractSofaHrir(sofa->hrtf, si, 1, src.mOffset, hrirPoints);
1877 azd->mIrs[1] = hrirs.subspan(
1878 (size_t{hData->mIrCount}+azd->mIndex) * hData->mIrSize, hData->mIrSize);
1879 azd->mDelays[1] = AverageHrirOnset(onsetResampler, onsetSamples,
1880 hData->mIrRate, hrirPoints, 1.0, azd->mDelays[1]);
1881 if(resampler)
1882 resampler->process(hrirPoints, hrir);
1883 AverageHrirMagnitude(hData->mFftSize, al::span{hrir}.first(irPoints), 1.0,
1884 azd->mIrs[1]);
1887 // TODO: Since some SOFA files contain minimum phase HRIRs,
1888 // it would be beneficial to check for per-measurement delays
1889 // (when available) to reconstruct the HRTDs.
1892 continue;
1895 if(!ReadIndexTriplet(tr, hData, &fi, &ei, &ai))
1896 return 0;
1897 if(!TrReadOperator(tr, "]"))
1898 return 0;
1899 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1901 if(!azd->mIrs[0].empty())
1903 TrErrorAt(tr, line, col, "Redefinition of source.\n");
1904 return 0;
1906 if(!TrReadOperator(tr, "="))
1907 return 0;
1909 while(true)
1911 SourceRefT src{};
1912 if(!ReadSourceRef(tr, &src))
1913 return 0;
1915 // TODO: Would be nice to display 'x of y files', but that would
1916 // require preparing the source refs first to get a total count
1917 // before loading them.
1918 ++count;
1919 printf("\rLoading sources... %d file%s", count, (count==1)?"":"s");
1920 fflush(stdout);
1922 if(!LoadSource(&src, hData->mIrRate, al::span{hrir}.first(hData->mIrPoints)))
1923 return 0;
1925 auto ti = uint{0};
1926 if(hData->mChannelType == CT_STEREO)
1928 const auto ident = TrReadIdent(tr);
1929 if(ident.empty())
1930 return 0;
1932 if(auto earopt = MatchTargetEar(ident))
1933 ti = *earopt;
1934 else
1936 TrErrorAt(tr, line, col, "Expected a target ear.\n");
1937 return 0;
1940 const auto hrirPoints = al::span{hrir}.first(hData->mIrPoints);
1941 azd->mIrs[ti] = hrirs.subspan((ti*size_t{hData->mIrCount}+azd->mIndex)*hData->mIrSize,
1942 hData->mIrSize);
1943 azd->mDelays[ti] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate,
1944 hrirPoints, 1.0/factor[ti], azd->mDelays[ti]);
1945 if(resampler)
1946 resampler->process(hrirPoints, hrir);
1947 AverageHrirMagnitude(hData->mFftSize, al::span{hrir}.first(irPoints), 1.0/factor[ti],
1948 azd->mIrs[ti]);
1949 factor[ti] += 1.0;
1950 if(!TrIsOperator(tr, "+"))
1951 break;
1952 TrReadOperator(tr, "+");
1954 if(hData->mChannelType == CT_STEREO)
1956 if(azd->mIrs[0].empty())
1958 TrErrorAt(tr, line, col, "Missing left ear source reference(s).\n");
1959 return 0;
1961 if(azd->mIrs[1].empty())
1963 TrErrorAt(tr, line, col, "Missing right ear source reference(s).\n");
1964 return 0;
1968 printf("\n");
1969 hrir.clear();
1970 if(resampler)
1972 hData->mIrRate = outRate;
1973 hData->mIrPoints = irPoints;
1974 resampler.reset();
1976 for(fi = 0;fi < hData->mFds.size();fi++)
1978 for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
1980 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
1982 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1983 if(!azd->mIrs[0].empty())
1984 break;
1986 if(ai < hData->mFds[fi].mEvs[ei].mAzs.size())
1987 break;
1989 if(ei >= hData->mFds[fi].mEvs.size())
1991 TrError(tr, "Missing source references [ %d, *, * ].\n", fi);
1992 return 0;
1994 hData->mFds[fi].mEvStart = ei;
1995 for(;ei < hData->mFds[fi].mEvs.size();ei++)
1997 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
1999 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
2001 if(azd->mIrs[0].empty())
2003 TrError(tr, "Missing source reference [ %d, %d, %d ].\n", fi, ei, ai);
2004 return 0;
2009 for(uint ti{0};ti < channels;ti++)
2011 for(fi = 0;fi < hData->mFds.size();fi++)
2013 for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
2015 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
2017 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
2018 azd->mIrs[ti] = hrirs.subspan(
2019 (ti*size_t{hData->mIrCount} + azd->mIndex) * hData->mIrSize,
2020 hData->mIrSize);
2025 if(!TrLoad(tr))
2027 gSofaCache.clear();
2028 return 1;
2031 TrError(tr, "Errant data at end of source list.\n");
2032 gSofaCache.clear();
2033 return 0;
2036 } /* namespace */
2038 bool LoadDefInput(std::istream &istream, const al::span<const char> startbytes,
2039 const std::string_view filename, const uint fftSize, const uint truncSize, const uint outRate,
2040 const ChannelModeT chanMode, HrirDataT *hData)
2042 TokenReaderT tr{istream};
2044 TrSetup(startbytes, filename, &tr);
2045 if(!ProcessMetrics(&tr, fftSize, truncSize, chanMode, hData)
2046 || !ProcessSources(&tr, hData, outRate))
2047 return false;
2049 return true;