Make sure a variable is set before use
[openal-soft.git] / utils / makemhr / loaddef.cpp
blobe8092363aca40c200b1f2d2225d0ee32f6d86163
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 <iterator>
34 #include <limits>
35 #include <memory>
36 #include <cstdarg>
37 #include <vector>
39 #include "alfstream.h"
40 #include "aloptional.h"
41 #include "alspan.h"
42 #include "alstring.h"
43 #include "makemhr.h"
44 #include "polyphase_resampler.h"
46 #include "mysofa.h"
48 // Constants for accessing the token reader's ring buffer.
49 #define TR_RING_BITS (16)
50 #define TR_RING_SIZE (1 << TR_RING_BITS)
51 #define TR_RING_MASK (TR_RING_SIZE - 1)
53 // The token reader's load interval in bytes.
54 #define TR_LOAD_SIZE (TR_RING_SIZE >> 2)
56 // Token reader state for parsing the data set definition.
57 struct TokenReaderT {
58 std::istream &mIStream;
59 const char *mName{};
60 uint mLine{};
61 uint mColumn{};
62 char mRing[TR_RING_SIZE]{};
63 std::streamsize mIn{};
64 std::streamsize mOut{};
66 TokenReaderT(std::istream &istream) noexcept : mIStream{istream} { }
67 TokenReaderT(const TokenReaderT&) = default;
71 // The maximum identifier length used when processing the data set
72 // definition.
73 #define MAX_IDENT_LEN (16)
75 // The limits for the listener's head 'radius' in the data set definition.
76 #define MIN_RADIUS (0.05)
77 #define MAX_RADIUS (0.15)
79 // The maximum number of channels that can be addressed for a WAVE file
80 // source listed in the data set definition.
81 #define MAX_WAVE_CHANNELS (65535)
83 // The limits to the byte size for a binary source listed in the definition
84 // file.
85 #define MIN_BIN_SIZE (2)
86 #define MAX_BIN_SIZE (4)
88 // The minimum number of significant bits for binary sources listed in the
89 // data set definition. The maximum is calculated from the byte size.
90 #define MIN_BIN_BITS (16)
92 // The limits to the number of significant bits for an ASCII source listed in
93 // the data set definition.
94 #define MIN_ASCII_BITS (16)
95 #define MAX_ASCII_BITS (32)
97 // The four-character-codes for RIFF/RIFX WAVE file chunks.
98 #define FOURCC_RIFF (0x46464952) // 'RIFF'
99 #define FOURCC_RIFX (0x58464952) // 'RIFX'
100 #define FOURCC_WAVE (0x45564157) // 'WAVE'
101 #define FOURCC_FMT (0x20746D66) // 'fmt '
102 #define FOURCC_DATA (0x61746164) // 'data'
103 #define FOURCC_LIST (0x5453494C) // 'LIST'
104 #define FOURCC_WAVL (0x6C766177) // 'wavl'
105 #define FOURCC_SLNT (0x746E6C73) // 'slnt'
107 // The supported wave formats.
108 #define WAVE_FORMAT_PCM (0x0001)
109 #define WAVE_FORMAT_IEEE_FLOAT (0x0003)
110 #define WAVE_FORMAT_EXTENSIBLE (0xFFFE)
113 enum ByteOrderT {
114 BO_NONE,
115 BO_LITTLE,
116 BO_BIG
119 // Source format for the references listed in the data set definition.
120 enum SourceFormatT {
121 SF_NONE,
122 SF_ASCII, // ASCII text file.
123 SF_BIN_LE, // Little-endian binary file.
124 SF_BIN_BE, // Big-endian binary file.
125 SF_WAVE, // RIFF/RIFX WAVE file.
126 SF_SOFA // Spatially Oriented Format for Accoustics (SOFA) file.
129 // Element types for the references listed in the data set definition.
130 enum ElementTypeT {
131 ET_NONE,
132 ET_INT, // Integer elements.
133 ET_FP // Floating-point elements.
136 // Source reference state used when loading sources.
137 struct SourceRefT {
138 SourceFormatT mFormat;
139 ElementTypeT mType;
140 uint mSize;
141 int mBits;
142 uint mChannel;
143 double mAzimuth;
144 double mElevation;
145 double mRadius;
146 uint mSkip;
147 uint mOffset;
148 char mPath[MAX_PATH_LEN+1];
152 /* Whitespace is not significant. It can process tokens as identifiers, numbers
153 * (integer and floating-point), strings, and operators. Strings must be
154 * encapsulated by double-quotes and cannot span multiple lines.
157 // Setup the reader on the given file. The filename can be NULL if no error
158 // output is desired.
159 static void TrSetup(const char *startbytes, std::streamsize startbytecount, const char *filename,
160 TokenReaderT *tr)
162 const char *name = nullptr;
164 if(filename)
166 const char *slash = strrchr(filename, '/');
167 if(slash)
169 const char *bslash = strrchr(slash+1, '\\');
170 if(bslash) name = bslash+1;
171 else name = slash+1;
173 else
175 const char *bslash = strrchr(filename, '\\');
176 if(bslash) name = bslash+1;
177 else name = filename;
181 tr->mName = name;
182 tr->mLine = 1;
183 tr->mColumn = 1;
184 tr->mIn = 0;
185 tr->mOut = 0;
187 if(startbytecount > 0)
189 std::copy_n(startbytes, startbytecount, std::begin(tr->mRing));
190 tr->mIn += startbytecount;
194 // Prime the reader's ring buffer, and return a result indicating that there
195 // is text to process.
196 static int TrLoad(TokenReaderT *tr)
198 std::istream &istream = tr->mIStream;
200 std::streamsize toLoad{TR_RING_SIZE - static_cast<std::streamsize>(tr->mIn - tr->mOut)};
201 if(toLoad >= TR_LOAD_SIZE && istream.good())
203 // Load TR_LOAD_SIZE (or less if at the end of the file) per read.
204 toLoad = TR_LOAD_SIZE;
205 std::streamsize in{tr->mIn&TR_RING_MASK};
206 std::streamsize count{TR_RING_SIZE - in};
207 if(count < toLoad)
209 istream.read(&tr->mRing[in], count);
210 tr->mIn += istream.gcount();
211 istream.read(&tr->mRing[0], toLoad-count);
212 tr->mIn += istream.gcount();
214 else
216 istream.read(&tr->mRing[in], toLoad);
217 tr->mIn += istream.gcount();
220 if(tr->mOut >= TR_RING_SIZE)
222 tr->mOut -= TR_RING_SIZE;
223 tr->mIn -= TR_RING_SIZE;
226 if(tr->mIn > tr->mOut)
227 return 1;
228 return 0;
231 // Error display routine. Only displays when the base name is not NULL.
232 static void TrErrorVA(const TokenReaderT *tr, uint line, uint column, const char *format, va_list argPtr)
234 if(!tr->mName)
235 return;
236 fprintf(stderr, "\nError (%s:%u:%u): ", tr->mName, line, column);
237 vfprintf(stderr, format, argPtr);
240 // Used to display an error at a saved line/column.
241 static void TrErrorAt(const TokenReaderT *tr, uint line, uint column, const char *format, ...)
243 va_list argPtr;
245 va_start(argPtr, format);
246 TrErrorVA(tr, line, column, format, argPtr);
247 va_end(argPtr);
250 // Used to display an error at the current line/column.
251 static void TrError(const TokenReaderT *tr, const char *format, ...)
253 va_list argPtr;
255 va_start(argPtr, format);
256 TrErrorVA(tr, tr->mLine, tr->mColumn, format, argPtr);
257 va_end(argPtr);
260 // Skips to the next line.
261 static void TrSkipLine(TokenReaderT *tr)
263 char ch;
265 while(TrLoad(tr))
267 ch = tr->mRing[tr->mOut&TR_RING_MASK];
268 tr->mOut++;
269 if(ch == '\n')
271 tr->mLine++;
272 tr->mColumn = 1;
273 break;
275 tr->mColumn ++;
279 // Skips to the next token.
280 static int TrSkipWhitespace(TokenReaderT *tr)
282 while(TrLoad(tr))
284 char ch{tr->mRing[tr->mOut&TR_RING_MASK]};
285 if(isspace(ch))
287 tr->mOut++;
288 if(ch == '\n')
290 tr->mLine++;
291 tr->mColumn = 1;
293 else
294 tr->mColumn++;
296 else if(ch == '#')
297 TrSkipLine(tr);
298 else
299 return 1;
301 return 0;
304 // Get the line and/or column of the next token (or the end of input).
305 static void TrIndication(TokenReaderT *tr, uint *line, uint *column)
307 TrSkipWhitespace(tr);
308 if(line) *line = tr->mLine;
309 if(column) *column = tr->mColumn;
312 // Checks to see if a token is (likely to be) an identifier. It does not
313 // display any errors and will not proceed to the next token.
314 static int TrIsIdent(TokenReaderT *tr)
316 if(!TrSkipWhitespace(tr))
317 return 0;
318 char ch{tr->mRing[tr->mOut&TR_RING_MASK]};
319 return ch == '_' || isalpha(ch);
323 // Checks to see if a token is the given operator. It does not display any
324 // errors and will not proceed to the next token.
325 static int TrIsOperator(TokenReaderT *tr, const char *op)
327 std::streamsize out;
328 size_t len;
329 char ch;
331 if(!TrSkipWhitespace(tr))
332 return 0;
333 out = tr->mOut;
334 len = 0;
335 while(op[len] != '\0' && out < tr->mIn)
337 ch = tr->mRing[out&TR_RING_MASK];
338 if(ch != op[len]) break;
339 len++;
340 out++;
342 if(op[len] == '\0')
343 return 1;
344 return 0;
347 /* The TrRead*() routines obtain the value of a matching token type. They
348 * display type, form, and boundary errors and will proceed to the next
349 * token.
352 // Reads and validates an identifier token.
353 static int TrReadIdent(TokenReaderT *tr, const uint maxLen, char *ident)
355 uint col, len;
356 char ch;
358 col = tr->mColumn;
359 if(TrSkipWhitespace(tr))
361 col = tr->mColumn;
362 ch = tr->mRing[tr->mOut&TR_RING_MASK];
363 if(ch == '_' || isalpha(ch))
365 len = 0;
366 do {
367 if(len < maxLen)
368 ident[len] = ch;
369 len++;
370 tr->mOut++;
371 if(!TrLoad(tr))
372 break;
373 ch = tr->mRing[tr->mOut&TR_RING_MASK];
374 } while(ch == '_' || isdigit(ch) || isalpha(ch));
376 tr->mColumn += len;
377 if(len < maxLen)
379 ident[len] = '\0';
380 return 1;
382 TrErrorAt(tr, tr->mLine, col, "Identifier is too long.\n");
383 return 0;
386 TrErrorAt(tr, tr->mLine, col, "Expected an identifier.\n");
387 return 0;
390 // Reads and validates (including bounds) an integer token.
391 static int TrReadInt(TokenReaderT *tr, const int loBound, const int hiBound, int *value)
393 uint col, digis, len;
394 char ch, temp[64+1];
396 col = tr->mColumn;
397 if(TrSkipWhitespace(tr))
399 col = tr->mColumn;
400 len = 0;
401 ch = tr->mRing[tr->mOut&TR_RING_MASK];
402 if(ch == '+' || ch == '-')
404 temp[len] = ch;
405 len++;
406 tr->mOut++;
408 digis = 0;
409 while(TrLoad(tr))
411 ch = tr->mRing[tr->mOut&TR_RING_MASK];
412 if(!isdigit(ch)) break;
413 if(len < 64)
414 temp[len] = ch;
415 len++;
416 digis++;
417 tr->mOut++;
419 tr->mColumn += len;
420 if(digis > 0 && ch != '.' && !isalpha(ch))
422 if(len > 64)
424 TrErrorAt(tr, tr->mLine, col, "Integer is too long.");
425 return 0;
427 temp[len] = '\0';
428 *value = static_cast<int>(strtol(temp, nullptr, 10));
429 if(*value < loBound || *value > hiBound)
431 TrErrorAt(tr, tr->mLine, col, "Expected a value from %d to %d.\n", loBound, hiBound);
432 return 0;
434 return 1;
437 TrErrorAt(tr, tr->mLine, col, "Expected an integer.\n");
438 return 0;
441 // Reads and validates (including bounds) a float token.
442 static int TrReadFloat(TokenReaderT *tr, const double loBound, const double hiBound, double *value)
444 uint col, digis, len;
445 char ch, temp[64+1];
447 col = tr->mColumn;
448 if(TrSkipWhitespace(tr))
450 col = tr->mColumn;
451 len = 0;
452 ch = tr->mRing[tr->mOut&TR_RING_MASK];
453 if(ch == '+' || ch == '-')
455 temp[len] = ch;
456 len++;
457 tr->mOut++;
460 digis = 0;
461 while(TrLoad(tr))
463 ch = tr->mRing[tr->mOut&TR_RING_MASK];
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&TR_RING_MASK];
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&TR_RING_MASK];
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, 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 static int TrReadString(TokenReaderT *tr, const uint maxLen, char *text)
543 uint col, len;
544 char ch;
546 col = tr->mColumn;
547 if(TrSkipWhitespace(tr))
549 col = tr->mColumn;
550 ch = tr->mRing[tr->mOut&TR_RING_MASK];
551 if(ch == '\"')
553 tr->mOut++;
554 len = 0;
555 while(TrLoad(tr))
557 ch = tr->mRing[tr->mOut&TR_RING_MASK];
558 tr->mOut++;
559 if(ch == '\"')
560 break;
561 if(ch == '\n')
563 TrErrorAt(tr, tr->mLine, col, "Unterminated string at end of line.\n");
564 return 0;
566 if(len < maxLen)
567 text[len] = ch;
568 len++;
570 if(ch != '\"')
572 tr->mColumn += 1 + len;
573 TrErrorAt(tr, tr->mLine, col, "Unterminated string at end of input.\n");
574 return 0;
576 tr->mColumn += 2 + len;
577 if(len > maxLen)
579 TrErrorAt(tr, tr->mLine, col, "String is too long.\n");
580 return 0;
582 text[len] = '\0';
583 return 1;
586 TrErrorAt(tr, tr->mLine, col, "Expected a string.\n");
587 return 0;
590 // Reads and validates the given operator.
591 static int TrReadOperator(TokenReaderT *tr, const char *op)
593 uint col, len;
594 char ch;
596 col = tr->mColumn;
597 if(TrSkipWhitespace(tr))
599 col = tr->mColumn;
600 len = 0;
601 while(op[len] != '\0' && TrLoad(tr))
603 ch = tr->mRing[tr->mOut&TR_RING_MASK];
604 if(ch != op[len]) break;
605 len++;
606 tr->mOut++;
608 tr->mColumn += len;
609 if(op[len] == '\0')
610 return 1;
612 TrErrorAt(tr, tr->mLine, col, "Expected '%s' operator.\n", op);
613 return 0;
617 /*************************
618 *** File source input ***
619 *************************/
621 // Read a binary value of the specified byte order and byte size from a file,
622 // storing it as a 32-bit unsigned integer.
623 static int ReadBin4(std::istream &istream, const char *filename, const ByteOrderT order, const uint bytes, uint32_t *out)
625 uint8_t in[4];
626 istream.read(reinterpret_cast<char*>(in), static_cast<int>(bytes));
627 if(istream.gcount() != bytes)
629 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
630 return 0;
632 uint32_t accum{0};
633 switch(order)
635 case BO_LITTLE:
636 for(uint i = 0;i < bytes;i++)
637 accum = (accum<<8) | in[bytes - i - 1];
638 break;
639 case BO_BIG:
640 for(uint i = 0;i < bytes;i++)
641 accum = (accum<<8) | in[i];
642 break;
643 default:
644 break;
646 *out = accum;
647 return 1;
650 // Read a binary value of the specified byte order from a file, storing it as
651 // a 64-bit unsigned integer.
652 static int ReadBin8(std::istream &istream, const char *filename, const ByteOrderT order, uint64_t *out)
654 uint8_t in[8];
655 uint64_t accum;
656 uint i;
658 istream.read(reinterpret_cast<char*>(in), 8);
659 if(istream.gcount() != 8)
661 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
662 return 0;
664 accum = 0;
665 switch(order)
667 case BO_LITTLE:
668 for(i = 0;i < 8;i++)
669 accum = (accum<<8) | in[8 - i - 1];
670 break;
671 case BO_BIG:
672 for(i = 0;i < 8;i++)
673 accum = (accum<<8) | in[i];
674 break;
675 default:
676 break;
678 *out = accum;
679 return 1;
682 /* Read a binary value of the specified type, byte order, and byte size from
683 * a file, converting it to a double. For integer types, the significant
684 * bits are used to normalize the result. The sign of bits determines
685 * whether they are padded toward the MSB (negative) or LSB (positive).
686 * Floating-point types are not normalized.
688 static int ReadBinAsDouble(std::istream &istream, const char *filename, const ByteOrderT order,
689 const ElementTypeT type, const uint bytes, const int bits, double *out)
691 union {
692 uint32_t ui;
693 int32_t i;
694 float f;
695 } v4;
696 union {
697 uint64_t ui;
698 double f;
699 } v8;
701 *out = 0.0;
702 if(bytes > 4)
704 if(!ReadBin8(istream, filename, order, &v8.ui))
705 return 0;
706 if(type == ET_FP)
707 *out = v8.f;
709 else
711 if(!ReadBin4(istream, filename, order, bytes, &v4.ui))
712 return 0;
713 if(type == ET_FP)
714 *out = v4.f;
715 else
717 if(bits > 0)
718 v4.ui >>= (8*bytes) - (static_cast<uint>(bits));
719 else
720 v4.ui &= (0xFFFFFFFF >> (32+bits));
722 if(v4.ui&static_cast<uint>(1<<(std::abs(bits)-1)))
723 v4.ui |= (0xFFFFFFFF << std::abs(bits));
724 *out = v4.i / static_cast<double>(1<<(std::abs(bits)-1));
727 return 1;
730 /* Read an ascii value of the specified type from a file, converting it to a
731 * double. For integer types, the significant bits are used to normalize the
732 * result. The sign of the bits should always be positive. This also skips
733 * up to one separator character before the element itself.
735 static int ReadAsciiAsDouble(TokenReaderT *tr, const char *filename, const ElementTypeT type, const uint bits, double *out)
737 if(TrIsOperator(tr, ","))
738 TrReadOperator(tr, ",");
739 else if(TrIsOperator(tr, ":"))
740 TrReadOperator(tr, ":");
741 else if(TrIsOperator(tr, ";"))
742 TrReadOperator(tr, ";");
743 else if(TrIsOperator(tr, "|"))
744 TrReadOperator(tr, "|");
746 if(type == ET_FP)
748 if(!TrReadFloat(tr, -std::numeric_limits<double>::infinity(),
749 std::numeric_limits<double>::infinity(), out))
751 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
752 return 0;
755 else
757 int v;
758 if(!TrReadInt(tr, -(1<<(bits-1)), (1<<(bits-1))-1, &v))
760 fprintf(stderr, "\nError: Bad read from file '%s'.\n", filename);
761 return 0;
763 *out = v / static_cast<double>((1<<(bits-1))-1);
765 return 1;
768 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
769 // the source parameters and data set metrics.
770 static int ReadWaveFormat(std::istream &istream, const ByteOrderT order, const uint hrirRate,
771 SourceRefT *src)
773 uint32_t fourCC, chunkSize;
774 uint32_t format, channels, rate, dummy, block, size, bits;
776 chunkSize = 0;
777 do {
778 if(chunkSize > 0)
779 istream.seekg(static_cast<int>(chunkSize), std::ios::cur);
780 if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC)
781 || !ReadBin4(istream, src->mPath, order, 4, &chunkSize))
782 return 0;
783 } while(fourCC != FOURCC_FMT);
784 if(!ReadBin4(istream, src->mPath, order, 2, &format)
785 || !ReadBin4(istream, src->mPath, order, 2, &channels)
786 || !ReadBin4(istream, src->mPath, order, 4, &rate)
787 || !ReadBin4(istream, src->mPath, order, 4, &dummy)
788 || !ReadBin4(istream, src->mPath, order, 2, &block))
789 return 0;
790 block /= channels;
791 if(chunkSize > 14)
793 if(!ReadBin4(istream, src->mPath, order, 2, &size))
794 return 0;
795 size /= 8;
796 if(block > size)
797 size = block;
799 else
800 size = block;
801 if(format == WAVE_FORMAT_EXTENSIBLE)
803 istream.seekg(2, std::ios::cur);
804 if(!ReadBin4(istream, src->mPath, order, 2, &bits))
805 return 0;
806 if(bits == 0)
807 bits = 8 * size;
808 istream.seekg(4, std::ios::cur);
809 if(!ReadBin4(istream, src->mPath, order, 2, &format))
810 return 0;
811 istream.seekg(static_cast<int>(chunkSize - 26), std::ios::cur);
813 else
815 bits = 8 * size;
816 if(chunkSize > 14)
817 istream.seekg(static_cast<int>(chunkSize - 16), std::ios::cur);
818 else
819 istream.seekg(static_cast<int>(chunkSize - 14), std::ios::cur);
821 if(format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_IEEE_FLOAT)
823 fprintf(stderr, "\nError: Unsupported WAVE format in file '%s'.\n", src->mPath);
824 return 0;
826 if(src->mChannel >= channels)
828 fprintf(stderr, "\nError: Missing source channel in WAVE file '%s'.\n", src->mPath);
829 return 0;
831 if(rate != hrirRate)
833 fprintf(stderr, "\nError: Mismatched source sample rate in WAVE file '%s'.\n", src->mPath);
834 return 0;
836 if(format == WAVE_FORMAT_PCM)
838 if(size < 2 || size > 4)
840 fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n", src->mPath);
841 return 0;
843 if(bits < 16 || bits > (8*size))
845 fprintf(stderr, "\nError: Bad significant bits in WAVE file '%s'.\n", src->mPath);
846 return 0;
848 src->mType = ET_INT;
850 else
852 if(size != 4 && size != 8)
854 fprintf(stderr, "\nError: Unsupported sample size in WAVE file '%s'.\n", src->mPath);
855 return 0;
857 src->mType = ET_FP;
859 src->mSize = size;
860 src->mBits = static_cast<int>(bits);
861 src->mSkip = channels;
862 return 1;
865 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
866 static int ReadWaveData(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
867 const uint n, double *hrir)
869 int pre, post, skip;
870 uint i;
872 pre = static_cast<int>(src->mSize * src->mChannel);
873 post = static_cast<int>(src->mSize * (src->mSkip - src->mChannel - 1));
874 skip = 0;
875 for(i = 0;i < n;i++)
877 skip += pre;
878 if(skip > 0)
879 istream.seekg(skip, std::ios::cur);
880 if(!ReadBinAsDouble(istream, src->mPath, order, src->mType, src->mSize, src->mBits, &hrir[i]))
881 return 0;
882 skip = post;
884 if(skip > 0)
885 istream.seekg(skip, std::ios::cur);
886 return 1;
889 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
890 // doubles.
891 static int ReadWaveList(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
892 const uint n, double *hrir)
894 uint32_t fourCC, chunkSize, listSize, count;
895 uint block, skip, offset, i;
896 double lastSample;
898 for(;;)
900 if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC)
901 || !ReadBin4(istream, src->mPath, order, 4, &chunkSize))
902 return 0;
904 if(fourCC == FOURCC_DATA)
906 block = src->mSize * src->mSkip;
907 count = chunkSize / block;
908 if(count < (src->mOffset + n))
910 fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath);
911 return 0;
913 istream.seekg(static_cast<long>(src->mOffset * block), std::ios::cur);
914 if(!ReadWaveData(istream, src, order, n, &hrir[0]))
915 return 0;
916 return 1;
918 else if(fourCC == FOURCC_LIST)
920 if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC))
921 return 0;
922 chunkSize -= 4;
923 if(fourCC == FOURCC_WAVL)
924 break;
926 if(chunkSize > 0)
927 istream.seekg(static_cast<long>(chunkSize), std::ios::cur);
929 listSize = chunkSize;
930 block = src->mSize * src->mSkip;
931 skip = src->mOffset;
932 offset = 0;
933 lastSample = 0.0;
934 while(offset < n && listSize > 8)
936 if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC)
937 || !ReadBin4(istream, src->mPath, order, 4, &chunkSize))
938 return 0;
939 listSize -= 8 + chunkSize;
940 if(fourCC == FOURCC_DATA)
942 count = chunkSize / block;
943 if(count > skip)
945 istream.seekg(static_cast<long>(skip * block), std::ios::cur);
946 chunkSize -= skip * block;
947 count -= skip;
948 skip = 0;
949 if(count > (n - offset))
950 count = n - offset;
951 if(!ReadWaveData(istream, src, order, count, &hrir[offset]))
952 return 0;
953 chunkSize -= count * block;
954 offset += count;
955 lastSample = hrir[offset - 1];
957 else
959 skip -= count;
960 count = 0;
963 else if(fourCC == FOURCC_SLNT)
965 if(!ReadBin4(istream, src->mPath, order, 4, &count))
966 return 0;
967 chunkSize -= 4;
968 if(count > skip)
970 count -= skip;
971 skip = 0;
972 if(count > (n - offset))
973 count = n - offset;
974 for(i = 0; i < count; i ++)
975 hrir[offset + i] = lastSample;
976 offset += count;
978 else
980 skip -= count;
981 count = 0;
984 if(chunkSize > 0)
985 istream.seekg(static_cast<long>(chunkSize), std::ios::cur);
987 if(offset < n)
989 fprintf(stderr, "\nError: Bad read from file '%s'.\n", src->mPath);
990 return 0;
992 return 1;
995 // Load a source HRIR from an ASCII text file containing a list of elements
996 // separated by whitespace or common list operators (',', ';', ':', '|').
997 static int LoadAsciiSource(std::istream &istream, const SourceRefT *src,
998 const uint n, double *hrir)
1000 TokenReaderT tr{istream};
1001 uint i, j;
1002 double dummy;
1004 TrSetup(nullptr, 0, nullptr, &tr);
1005 for(i = 0;i < src->mOffset;i++)
1007 if(!ReadAsciiAsDouble(&tr, src->mPath, src->mType, static_cast<uint>(src->mBits), &dummy))
1008 return 0;
1010 for(i = 0;i < n;i++)
1012 if(!ReadAsciiAsDouble(&tr, src->mPath, src->mType, static_cast<uint>(src->mBits), &hrir[i]))
1013 return 0;
1014 for(j = 0;j < src->mSkip;j++)
1016 if(!ReadAsciiAsDouble(&tr, src->mPath, src->mType, static_cast<uint>(src->mBits), &dummy))
1017 return 0;
1020 return 1;
1023 // Load a source HRIR from a binary file.
1024 static int LoadBinarySource(std::istream &istream, const SourceRefT *src, const ByteOrderT order,
1025 const uint n, double *hrir)
1027 istream.seekg(static_cast<long>(src->mOffset), std::ios::beg);
1028 for(uint i{0};i < n;i++)
1030 if(!ReadBinAsDouble(istream, src->mPath, order, src->mType, src->mSize, src->mBits, &hrir[i]))
1031 return 0;
1032 if(src->mSkip > 0)
1033 istream.seekg(static_cast<long>(src->mSkip), std::ios::cur);
1035 return 1;
1038 // Load a source HRIR from a RIFF/RIFX WAVE file.
1039 static int LoadWaveSource(std::istream &istream, SourceRefT *src, const uint hrirRate,
1040 const uint n, double *hrir)
1042 uint32_t fourCC, dummy;
1043 ByteOrderT order;
1045 if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC)
1046 || !ReadBin4(istream, src->mPath, BO_LITTLE, 4, &dummy))
1047 return 0;
1048 if(fourCC == FOURCC_RIFF)
1049 order = BO_LITTLE;
1050 else if(fourCC == FOURCC_RIFX)
1051 order = BO_BIG;
1052 else
1054 fprintf(stderr, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src->mPath);
1055 return 0;
1058 if(!ReadBin4(istream, src->mPath, BO_LITTLE, 4, &fourCC))
1059 return 0;
1060 if(fourCC != FOURCC_WAVE)
1062 fprintf(stderr, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src->mPath);
1063 return 0;
1065 if(!ReadWaveFormat(istream, order, hrirRate, src))
1066 return 0;
1067 if(!ReadWaveList(istream, src, order, n, hrir))
1068 return 0;
1069 return 1;
1074 // Load a Spatially Oriented Format for Accoustics (SOFA) file.
1075 static MYSOFA_EASY* LoadSofaFile(SourceRefT *src, const uint hrirRate, const uint n)
1077 struct MYSOFA_EASY *sofa{mysofa_cache_lookup(src->mPath, static_cast<float>(hrirRate))};
1078 if(sofa) return sofa;
1080 sofa = static_cast<MYSOFA_EASY*>(calloc(1, sizeof(*sofa)));
1081 if(sofa == nullptr)
1083 fprintf(stderr, "\nError: Out of memory.\n");
1084 return nullptr;
1086 sofa->lookup = nullptr;
1087 sofa->neighborhood = nullptr;
1089 int err;
1090 sofa->hrtf = mysofa_load(src->mPath, &err);
1091 if(!sofa->hrtf)
1093 mysofa_close(sofa);
1094 fprintf(stderr, "\nError: Could not load source file '%s'.\n", src->mPath);
1095 return nullptr;
1097 /* NOTE: Some valid SOFA files are failing this check. */
1098 err = mysofa_check(sofa->hrtf);
1099 if(err != MYSOFA_OK)
1100 fprintf(stderr, "\nWarning: Supposedly malformed source file '%s'.\n", src->mPath);
1101 if((src->mOffset + n) > sofa->hrtf->N)
1103 mysofa_close(sofa);
1104 fprintf(stderr, "\nError: Not enough samples in SOFA file '%s'.\n", src->mPath);
1105 return nullptr;
1107 if(src->mChannel >= sofa->hrtf->R)
1109 mysofa_close(sofa);
1110 fprintf(stderr, "\nError: Missing source receiver in SOFA file '%s'.\n", src->mPath);
1111 return nullptr;
1113 mysofa_tocartesian(sofa->hrtf);
1114 sofa->lookup = mysofa_lookup_init(sofa->hrtf);
1115 if(sofa->lookup == nullptr)
1117 mysofa_close(sofa);
1118 fprintf(stderr, "\nError: Out of memory.\n");
1119 return nullptr;
1121 return mysofa_cache_store(sofa, src->mPath, static_cast<float>(hrirRate));
1124 // Copies the HRIR data from a particular SOFA measurement.
1125 static void ExtractSofaHrir(const MYSOFA_EASY *sofa, const uint index, const uint channel, const uint offset, const uint n, double *hrir)
1127 for(uint i{0u};i < n;i++)
1128 hrir[i] = sofa->hrtf->DataIR.values[(index*sofa->hrtf->R + channel)*sofa->hrtf->N + offset + i];
1131 // Load a source HRIR from a Spatially Oriented Format for Accoustics (SOFA)
1132 // file.
1133 static int LoadSofaSource(SourceRefT *src, const uint hrirRate, const uint n, double *hrir)
1135 struct MYSOFA_EASY *sofa;
1136 float target[3];
1137 int nearest;
1138 float *coords;
1140 sofa = LoadSofaFile(src, hrirRate, n);
1141 if(sofa == nullptr)
1142 return 0;
1144 /* NOTE: At some point it may be benficial or necessary to consider the
1145 various coordinate systems, listener/source orientations, and
1146 direciontal vectors defined in the SOFA file.
1148 target[0] = static_cast<float>(src->mAzimuth);
1149 target[1] = static_cast<float>(src->mElevation);
1150 target[2] = static_cast<float>(src->mRadius);
1151 mysofa_s2c(target);
1153 nearest = mysofa_lookup(sofa->lookup, target);
1154 if(nearest < 0)
1156 fprintf(stderr, "\nError: Lookup failed in source file '%s'.\n", src->mPath);
1157 return 0;
1160 coords = &sofa->hrtf->SourcePosition.values[3 * nearest];
1161 if(std::abs(coords[0] - target[0]) > 0.001 || std::abs(coords[1] - target[1]) > 0.001 || std::abs(coords[2] - target[2]) > 0.001)
1163 fprintf(stderr, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n", src->mRadius, src->mElevation, src->mAzimuth, src->mPath);
1164 target[0] = coords[0];
1165 target[1] = coords[1];
1166 target[2] = coords[2];
1167 mysofa_c2s(target);
1168 fprintf(stderr, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target[2], target[1], target[0]);
1169 return 0;
1172 ExtractSofaHrir(sofa, static_cast<uint>(nearest), src->mChannel, src->mOffset, n, hrir);
1174 return 1;
1177 // Load a source HRIR from a supported file type.
1178 static int LoadSource(SourceRefT *src, const uint hrirRate, const uint n, double *hrir)
1180 std::unique_ptr<al::ifstream> istream;
1181 if(src->mFormat != SF_SOFA)
1183 if(src->mFormat == SF_ASCII)
1184 istream.reset(new al::ifstream{src->mPath});
1185 else
1186 istream.reset(new al::ifstream{src->mPath, std::ios::binary});
1187 if(!istream->good())
1189 fprintf(stderr, "\nError: Could not open source file '%s'.\n", src->mPath);
1190 return 0;
1193 int result{0};
1194 switch(src->mFormat)
1196 case SF_ASCII:
1197 result = LoadAsciiSource(*istream, src, n, hrir);
1198 break;
1199 case SF_BIN_LE:
1200 result = LoadBinarySource(*istream, src, BO_LITTLE, n, hrir);
1201 break;
1202 case SF_BIN_BE:
1203 result = LoadBinarySource(*istream, src, BO_BIG, n, hrir);
1204 break;
1205 case SF_WAVE:
1206 result = LoadWaveSource(*istream, src, hrirRate, n, hrir);
1207 break;
1208 case SF_SOFA:
1209 result = LoadSofaSource(src, hrirRate, n, hrir);
1210 break;
1211 case SF_NONE:
1212 break;
1214 return result;
1218 // Match the channel type from a given identifier.
1219 static ChannelTypeT MatchChannelType(const char *ident)
1221 if(al::strcasecmp(ident, "mono") == 0)
1222 return CT_MONO;
1223 if(al::strcasecmp(ident, "stereo") == 0)
1224 return CT_STEREO;
1225 return CT_NONE;
1229 // Process the data set definition to read and validate the data set metrics.
1230 static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint truncSize, const ChannelModeT chanMode, HrirDataT *hData)
1232 int hasRate = 0, hasType = 0, hasPoints = 0, hasRadius = 0;
1233 int hasDistance = 0, hasAzimuths = 0;
1234 char ident[MAX_IDENT_LEN+1];
1235 uint line, col;
1236 double fpVal;
1237 uint points;
1238 int intVal;
1239 double distances[MAX_FD_COUNT];
1240 uint fdCount = 0;
1241 uint evCounts[MAX_FD_COUNT];
1242 auto azCounts = std::vector<std::array<uint,MAX_EV_COUNT>>(MAX_FD_COUNT);
1243 for(auto &azs : azCounts) azs.fill(0u);
1245 TrIndication(tr, &line, &col);
1246 while(TrIsIdent(tr))
1248 TrIndication(tr, &line, &col);
1249 if(!TrReadIdent(tr, MAX_IDENT_LEN, ident))
1250 return 0;
1251 if(al::strcasecmp(ident, "rate") == 0)
1253 if(hasRate)
1255 TrErrorAt(tr, line, col, "Redefinition of 'rate'.\n");
1256 return 0;
1258 if(!TrReadOperator(tr, "="))
1259 return 0;
1260 if(!TrReadInt(tr, MIN_RATE, MAX_RATE, &intVal))
1261 return 0;
1262 hData->mIrRate = static_cast<uint>(intVal);
1263 hasRate = 1;
1265 else if(al::strcasecmp(ident, "type") == 0)
1267 char type[MAX_IDENT_LEN+1];
1269 if(hasType)
1271 TrErrorAt(tr, line, col, "Redefinition of 'type'.\n");
1272 return 0;
1274 if(!TrReadOperator(tr, "="))
1275 return 0;
1277 if(!TrReadIdent(tr, MAX_IDENT_LEN, type))
1278 return 0;
1279 hData->mChannelType = MatchChannelType(type);
1280 if(hData->mChannelType == CT_NONE)
1282 TrErrorAt(tr, line, col, "Expected a channel type.\n");
1283 return 0;
1285 else if(hData->mChannelType == CT_STEREO)
1287 if(chanMode == CM_ForceMono)
1288 hData->mChannelType = CT_MONO;
1290 hasType = 1;
1292 else if(al::strcasecmp(ident, "points") == 0)
1294 if(hasPoints)
1296 TrErrorAt(tr, line, col, "Redefinition of 'points'.\n");
1297 return 0;
1299 if(!TrReadOperator(tr, "="))
1300 return 0;
1301 TrIndication(tr, &line, &col);
1302 if(!TrReadInt(tr, MIN_POINTS, MAX_POINTS, &intVal))
1303 return 0;
1304 points = static_cast<uint>(intVal);
1305 if(fftSize > 0 && points > fftSize)
1307 TrErrorAt(tr, line, col, "Value exceeds the overridden FFT size.\n");
1308 return 0;
1310 if(points < truncSize)
1312 TrErrorAt(tr, line, col, "Value is below the truncation size.\n");
1313 return 0;
1315 hData->mIrPoints = points;
1316 hData->mFftSize = fftSize;
1317 hData->mIrSize = 1 + (fftSize / 2);
1318 if(points > hData->mIrSize)
1319 hData->mIrSize = points;
1320 hasPoints = 1;
1322 else if(al::strcasecmp(ident, "radius") == 0)
1324 if(hasRadius)
1326 TrErrorAt(tr, line, col, "Redefinition of 'radius'.\n");
1327 return 0;
1329 if(!TrReadOperator(tr, "="))
1330 return 0;
1331 if(!TrReadFloat(tr, MIN_RADIUS, MAX_RADIUS, &fpVal))
1332 return 0;
1333 hData->mRadius = fpVal;
1334 hasRadius = 1;
1336 else if(al::strcasecmp(ident, "distance") == 0)
1338 uint count = 0;
1340 if(hasDistance)
1342 TrErrorAt(tr, line, col, "Redefinition of 'distance'.\n");
1343 return 0;
1345 if(!TrReadOperator(tr, "="))
1346 return 0;
1348 for(;;)
1350 if(!TrReadFloat(tr, MIN_DISTANCE, MAX_DISTANCE, &fpVal))
1351 return 0;
1352 if(count > 0 && fpVal <= distances[count - 1])
1354 TrError(tr, "Distances are not ascending.\n");
1355 return 0;
1357 distances[count++] = fpVal;
1358 if(!TrIsOperator(tr, ","))
1359 break;
1360 if(count >= MAX_FD_COUNT)
1362 TrError(tr, "Exceeded the maximum of %d fields.\n", MAX_FD_COUNT);
1363 return 0;
1365 TrReadOperator(tr, ",");
1367 if(fdCount != 0 && count != fdCount)
1369 TrError(tr, "Did not match the specified number of %d fields.\n", fdCount);
1370 return 0;
1372 fdCount = count;
1373 hasDistance = 1;
1375 else if(al::strcasecmp(ident, "azimuths") == 0)
1377 uint count = 0;
1379 if(hasAzimuths)
1381 TrErrorAt(tr, line, col, "Redefinition of 'azimuths'.\n");
1382 return 0;
1384 if(!TrReadOperator(tr, "="))
1385 return 0;
1387 evCounts[0] = 0;
1388 for(;;)
1390 if(!TrReadInt(tr, MIN_AZ_COUNT, MAX_AZ_COUNT, &intVal))
1391 return 0;
1392 azCounts[count][evCounts[count]++] = static_cast<uint>(intVal);
1393 if(TrIsOperator(tr, ","))
1395 if(evCounts[count] >= MAX_EV_COUNT)
1397 TrError(tr, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT);
1398 return 0;
1400 TrReadOperator(tr, ",");
1402 else
1404 if(evCounts[count] < MIN_EV_COUNT)
1406 TrErrorAt(tr, line, col, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT);
1407 return 0;
1409 if(azCounts[count][0] != 1 || azCounts[count][evCounts[count] - 1] != 1)
1411 TrError(tr, "Poles are not singular for field %d.\n", count - 1);
1412 return 0;
1414 count++;
1415 if(!TrIsOperator(tr, ";"))
1416 break;
1418 if(count >= MAX_FD_COUNT)
1420 TrError(tr, "Exceeded the maximum number of %d fields.\n", MAX_FD_COUNT);
1421 return 0;
1423 evCounts[count] = 0;
1424 TrReadOperator(tr, ";");
1427 if(fdCount != 0 && count != fdCount)
1429 TrError(tr, "Did not match the specified number of %d fields.\n", fdCount);
1430 return 0;
1432 fdCount = count;
1433 hasAzimuths = 1;
1435 else
1437 TrErrorAt(tr, line, col, "Expected a metric name.\n");
1438 return 0;
1440 TrSkipWhitespace(tr);
1442 if(!(hasRate && hasPoints && hasRadius && hasDistance && hasAzimuths))
1444 TrErrorAt(tr, line, col, "Expected a metric name.\n");
1445 return 0;
1447 if(distances[0] < hData->mRadius)
1449 TrError(tr, "Distance cannot start below head radius.\n");
1450 return 0;
1452 if(hData->mChannelType == CT_NONE)
1453 hData->mChannelType = CT_MONO;
1454 const auto azs = al::as_span(azCounts).first<MAX_FD_COUNT>();
1455 if(!PrepareHrirData({distances, fdCount}, evCounts, azs, hData))
1457 fprintf(stderr, "Error: Out of memory.\n");
1458 exit(-1);
1460 return 1;
1463 // Parse an index triplet from the data set definition.
1464 static int ReadIndexTriplet(TokenReaderT *tr, const HrirDataT *hData, uint *fi, uint *ei, uint *ai)
1466 int intVal;
1468 if(hData->mFds.size() > 1)
1470 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds.size()-1), &intVal))
1471 return 0;
1472 *fi = static_cast<uint>(intVal);
1473 if(!TrReadOperator(tr, ","))
1474 return 0;
1476 else
1478 *fi = 0;
1480 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds[*fi].mEvs.size()-1), &intVal))
1481 return 0;
1482 *ei = static_cast<uint>(intVal);
1483 if(!TrReadOperator(tr, ","))
1484 return 0;
1485 if(!TrReadInt(tr, 0, static_cast<int>(hData->mFds[*fi].mEvs[*ei].mAzs.size()-1), &intVal))
1486 return 0;
1487 *ai = static_cast<uint>(intVal);
1488 return 1;
1491 // Match the source format from a given identifier.
1492 static SourceFormatT MatchSourceFormat(const char *ident)
1494 if(al::strcasecmp(ident, "ascii") == 0)
1495 return SF_ASCII;
1496 if(al::strcasecmp(ident, "bin_le") == 0)
1497 return SF_BIN_LE;
1498 if(al::strcasecmp(ident, "bin_be") == 0)
1499 return SF_BIN_BE;
1500 if(al::strcasecmp(ident, "wave") == 0)
1501 return SF_WAVE;
1502 if(al::strcasecmp(ident, "sofa") == 0)
1503 return SF_SOFA;
1504 return SF_NONE;
1507 // Match the source element type from a given identifier.
1508 static ElementTypeT MatchElementType(const char *ident)
1510 if(al::strcasecmp(ident, "int") == 0)
1511 return ET_INT;
1512 if(al::strcasecmp(ident, "fp") == 0)
1513 return ET_FP;
1514 return ET_NONE;
1517 // Parse and validate a source reference from the data set definition.
1518 static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src)
1520 char ident[MAX_IDENT_LEN+1];
1521 uint line, col;
1522 double fpVal;
1523 int intVal;
1525 TrIndication(tr, &line, &col);
1526 if(!TrReadIdent(tr, MAX_IDENT_LEN, ident))
1527 return 0;
1528 src->mFormat = MatchSourceFormat(ident);
1529 if(src->mFormat == SF_NONE)
1531 TrErrorAt(tr, line, col, "Expected a source format.\n");
1532 return 0;
1534 if(!TrReadOperator(tr, "("))
1535 return 0;
1536 if(src->mFormat == SF_SOFA)
1538 if(!TrReadFloat(tr, MIN_DISTANCE, MAX_DISTANCE, &fpVal))
1539 return 0;
1540 src->mRadius = fpVal;
1541 if(!TrReadOperator(tr, ","))
1542 return 0;
1543 if(!TrReadFloat(tr, -90.0, 90.0, &fpVal))
1544 return 0;
1545 src->mElevation = fpVal;
1546 if(!TrReadOperator(tr, ","))
1547 return 0;
1548 if(!TrReadFloat(tr, -360.0, 360.0, &fpVal))
1549 return 0;
1550 src->mAzimuth = fpVal;
1551 if(!TrReadOperator(tr, ":"))
1552 return 0;
1553 if(!TrReadInt(tr, 0, MAX_WAVE_CHANNELS, &intVal))
1554 return 0;
1555 src->mType = ET_NONE;
1556 src->mSize = 0;
1557 src->mBits = 0;
1558 src->mChannel = static_cast<uint>(intVal);
1559 src->mSkip = 0;
1561 else if(src->mFormat == SF_WAVE)
1563 if(!TrReadInt(tr, 0, MAX_WAVE_CHANNELS, &intVal))
1564 return 0;
1565 src->mType = ET_NONE;
1566 src->mSize = 0;
1567 src->mBits = 0;
1568 src->mChannel = static_cast<uint>(intVal);
1569 src->mSkip = 0;
1571 else
1573 TrIndication(tr, &line, &col);
1574 if(!TrReadIdent(tr, MAX_IDENT_LEN, ident))
1575 return 0;
1576 src->mType = MatchElementType(ident);
1577 if(src->mType == ET_NONE)
1579 TrErrorAt(tr, line, col, "Expected a source element type.\n");
1580 return 0;
1582 if(src->mFormat == SF_BIN_LE || src->mFormat == SF_BIN_BE)
1584 if(!TrReadOperator(tr, ","))
1585 return 0;
1586 if(src->mType == ET_INT)
1588 if(!TrReadInt(tr, MIN_BIN_SIZE, MAX_BIN_SIZE, &intVal))
1589 return 0;
1590 src->mSize = static_cast<uint>(intVal);
1591 if(!TrIsOperator(tr, ","))
1592 src->mBits = static_cast<int>(8*src->mSize);
1593 else
1595 TrReadOperator(tr, ",");
1596 TrIndication(tr, &line, &col);
1597 if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal))
1598 return 0;
1599 if(std::abs(intVal) < MIN_BIN_BITS || static_cast<uint>(std::abs(intVal)) > (8*src->mSize))
1601 TrErrorAt(tr, line, col, "Expected a value of (+/-) %d to %d.\n", MIN_BIN_BITS, 8*src->mSize);
1602 return 0;
1604 src->mBits = intVal;
1607 else
1609 TrIndication(tr, &line, &col);
1610 if(!TrReadInt(tr, -2147483647-1, 2147483647, &intVal))
1611 return 0;
1612 if(intVal != 4 && intVal != 8)
1614 TrErrorAt(tr, line, col, "Expected a value of 4 or 8.\n");
1615 return 0;
1617 src->mSize = static_cast<uint>(intVal);
1618 src->mBits = 0;
1621 else if(src->mFormat == SF_ASCII && src->mType == ET_INT)
1623 if(!TrReadOperator(tr, ","))
1624 return 0;
1625 if(!TrReadInt(tr, MIN_ASCII_BITS, MAX_ASCII_BITS, &intVal))
1626 return 0;
1627 src->mSize = 0;
1628 src->mBits = intVal;
1630 else
1632 src->mSize = 0;
1633 src->mBits = 0;
1636 if(!TrIsOperator(tr, ";"))
1637 src->mSkip = 0;
1638 else
1640 TrReadOperator(tr, ";");
1641 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1642 return 0;
1643 src->mSkip = static_cast<uint>(intVal);
1646 if(!TrReadOperator(tr, ")"))
1647 return 0;
1648 if(TrIsOperator(tr, "@"))
1650 TrReadOperator(tr, "@");
1651 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1652 return 0;
1653 src->mOffset = static_cast<uint>(intVal);
1655 else
1656 src->mOffset = 0;
1657 if(!TrReadOperator(tr, ":"))
1658 return 0;
1659 if(!TrReadString(tr, MAX_PATH_LEN, src->mPath))
1660 return 0;
1661 return 1;
1664 // Parse and validate a SOFA source reference from the data set definition.
1665 static int ReadSofaRef(TokenReaderT *tr, SourceRefT *src)
1667 char ident[MAX_IDENT_LEN+1];
1668 uint line, col;
1669 int intVal;
1671 TrIndication(tr, &line, &col);
1672 if(!TrReadIdent(tr, MAX_IDENT_LEN, ident))
1673 return 0;
1674 src->mFormat = MatchSourceFormat(ident);
1675 if(src->mFormat != SF_SOFA)
1677 TrErrorAt(tr, line, col, "Expected the SOFA source format.\n");
1678 return 0;
1681 src->mType = ET_NONE;
1682 src->mSize = 0;
1683 src->mBits = 0;
1684 src->mChannel = 0;
1685 src->mSkip = 0;
1687 if(TrIsOperator(tr, "@"))
1689 TrReadOperator(tr, "@");
1690 if(!TrReadInt(tr, 0, 0x7FFFFFFF, &intVal))
1691 return 0;
1692 src->mOffset = static_cast<uint>(intVal);
1694 else
1695 src->mOffset = 0;
1696 if(!TrReadOperator(tr, ":"))
1697 return 0;
1698 if(!TrReadString(tr, MAX_PATH_LEN, src->mPath))
1699 return 0;
1700 return 1;
1703 // Match the target ear (index) from a given identifier.
1704 static int MatchTargetEar(const char *ident)
1706 if(al::strcasecmp(ident, "left") == 0)
1707 return 0;
1708 if(al::strcasecmp(ident, "right") == 0)
1709 return 1;
1710 return -1;
1713 // Calculate the onset time of an HRIR and average it with any existing
1714 // timing for its field, elevation, azimuth, and ear.
1715 static constexpr int OnsetRateMultiple{10};
1716 static double AverageHrirOnset(PPhaseResampler &rs, al::span<double> upsampled, const uint rate,
1717 const uint n, const double *hrir, const double f, const double onset)
1719 rs.process(n, hrir, static_cast<uint>(upsampled.size()), upsampled.data());
1721 auto abs_lt = [](const double &lhs, const double &rhs) -> bool
1722 { return std::abs(lhs) < std::abs(rhs); };
1723 auto iter = std::max_element(upsampled.cbegin(), upsampled.cend(), abs_lt);
1724 return Lerp(onset, static_cast<double>(std::distance(upsampled.cbegin(), iter))/(10*rate), f);
1727 // Calculate the magnitude response of an HRIR and average it with any
1728 // existing responses for its field, elevation, azimuth, and ear.
1729 static void AverageHrirMagnitude(const uint points, const uint n, const double *hrir, const double f, double *mag)
1731 uint m = 1 + (n / 2), i;
1732 std::vector<complex_d> h(n);
1733 std::vector<double> r(n);
1735 for(i = 0;i < points;i++)
1736 h[i] = hrir[i];
1737 for(;i < n;i++)
1738 h[i] = 0.0;
1739 FftForward(n, h.data());
1740 MagnitudeResponse(n, h.data(), r.data());
1741 for(i = 0;i < m;i++)
1742 mag[i] = Lerp(mag[i], r[i], f);
1745 // Process the list of sources in the data set definition.
1746 static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate)
1748 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
1749 hData->mHrirsBase.resize(channels * hData->mIrCount * hData->mIrSize);
1750 double *hrirs = hData->mHrirsBase.data();
1751 auto hrir = std::make_unique<double[]>(hData->mIrSize);
1752 uint line, col, fi, ei, ai;
1754 std::vector<double> onsetSamples(OnsetRateMultiple * hData->mIrPoints);
1755 PPhaseResampler onsetResampler;
1756 onsetResampler.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate);
1758 al::optional<PPhaseResampler> resampler;
1759 if(outRate && outRate != hData->mIrRate)
1760 resampler.emplace().init(hData->mIrRate, outRate);
1761 const double rateScale{outRate ? static_cast<double>(outRate) / hData->mIrRate : 1.0};
1762 const uint irPoints{outRate
1763 ? std::min(static_cast<uint>(std::ceil(hData->mIrPoints*rateScale)), hData->mIrPoints)
1764 : hData->mIrPoints};
1766 printf("Loading sources...");
1767 fflush(stdout);
1768 int count{0};
1769 while(TrIsOperator(tr, "["))
1771 double factor[2]{ 1.0, 1.0 };
1773 TrIndication(tr, &line, &col);
1774 TrReadOperator(tr, "[");
1776 if(TrIsOperator(tr, "*"))
1778 SourceRefT src;
1779 struct MYSOFA_EASY *sofa;
1780 uint si;
1782 TrReadOperator(tr, "*");
1783 if(!TrReadOperator(tr, "]") || !TrReadOperator(tr, "="))
1784 return 0;
1786 TrIndication(tr, &line, &col);
1787 if(!ReadSofaRef(tr, &src))
1788 return 0;
1790 if(hData->mChannelType == CT_STEREO)
1792 char type[MAX_IDENT_LEN+1];
1793 ChannelTypeT channelType;
1795 if(!TrReadIdent(tr, MAX_IDENT_LEN, type))
1796 return 0;
1798 channelType = MatchChannelType(type);
1800 switch(channelType)
1802 case CT_NONE:
1803 TrErrorAt(tr, line, col, "Expected a channel type.\n");
1804 return 0;
1805 case CT_MONO:
1806 src.mChannel = 0;
1807 break;
1808 case CT_STEREO:
1809 src.mChannel = 1;
1810 break;
1813 else
1815 char type[MAX_IDENT_LEN+1];
1816 ChannelTypeT channelType;
1818 if(!TrReadIdent(tr, MAX_IDENT_LEN, type))
1819 return 0;
1821 channelType = MatchChannelType(type);
1822 if(channelType != CT_MONO)
1824 TrErrorAt(tr, line, col, "Expected a mono channel type.\n");
1825 return 0;
1827 src.mChannel = 0;
1830 sofa = LoadSofaFile(&src, hData->mIrRate, hData->mIrPoints);
1831 if(!sofa) return 0;
1833 for(si = 0;si < sofa->hrtf->M;si++)
1835 printf("\rLoading sources... %d of %d", si+1, sofa->hrtf->M);
1836 fflush(stdout);
1838 float aer[3] = {
1839 sofa->hrtf->SourcePosition.values[3*si],
1840 sofa->hrtf->SourcePosition.values[3*si + 1],
1841 sofa->hrtf->SourcePosition.values[3*si + 2]
1843 mysofa_c2s(aer);
1845 if(std::fabs(aer[1]) >= 89.999f)
1846 aer[0] = 0.0f;
1847 else
1848 aer[0] = std::fmod(360.0f - aer[0], 360.0f);
1850 auto field = std::find_if(hData->mFds.cbegin(), hData->mFds.cend(),
1851 [&aer](const HrirFdT &fld) -> bool
1852 { return (std::abs(aer[2] - fld.mDistance) < 0.001); });
1853 if(field == hData->mFds.cend())
1854 continue;
1855 fi = static_cast<uint>(std::distance(hData->mFds.cbegin(), field));
1857 const double evscale{180.0 / static_cast<double>(field->mEvs.size()-1)};
1858 double ef{(90.0 + aer[1]) / evscale};
1859 ei = static_cast<uint>(std::round(ef));
1860 ef = (ef - ei) * evscale;
1861 if(std::abs(ef) >= 0.1)
1862 continue;
1864 const double azscale{360.0 / static_cast<double>(field->mEvs[ei].mAzs.size())};
1865 double af{aer[0] / azscale};
1866 ai = static_cast<uint>(std::round(af));
1867 af = (af - ai) * azscale;
1868 ai %= static_cast<uint>(field->mEvs[ei].mAzs.size());
1869 if(std::abs(af) >= 0.1)
1870 continue;
1872 HrirAzT *azd = &field->mEvs[ei].mAzs[ai];
1873 if(azd->mIrs[0] != nullptr)
1875 TrErrorAt(tr, line, col, "Redefinition of source [ %d, %d, %d ].\n", fi, ei, ai);
1876 return 0;
1879 ExtractSofaHrir(sofa, si, 0, src.mOffset, hData->mIrPoints, hrir.get());
1880 azd->mIrs[0] = &hrirs[hData->mIrSize * azd->mIndex];
1881 azd->mDelays[0] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate,
1882 hData->mIrPoints, hrir.get(), 1.0, azd->mDelays[0]);
1883 if(resampler)
1884 resampler->process(hData->mIrPoints, hrir.get(), hData->mIrSize, hrir.get());
1885 AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.get(), 1.0, azd->mIrs[0]);
1887 if(src.mChannel == 1)
1889 ExtractSofaHrir(sofa, si, 1, src.mOffset, hData->mIrPoints, hrir.get());
1890 azd->mIrs[1] = &hrirs[hData->mIrSize * (hData->mIrCount + azd->mIndex)];
1891 azd->mDelays[1] = AverageHrirOnset(onsetResampler, onsetSamples,
1892 hData->mIrRate, hData->mIrPoints, hrir.get(), 1.0, azd->mDelays[1]);
1893 if(resampler)
1894 resampler->process(hData->mIrPoints, hrir.get(), hData->mIrSize,
1895 hrir.get());
1896 AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.get(), 1.0, azd->mIrs[1]);
1899 // TODO: Since some SOFA files contain minimum phase HRIRs,
1900 // it would be beneficial to check for per-measurement delays
1901 // (when available) to reconstruct the HRTDs.
1904 continue;
1907 if(!ReadIndexTriplet(tr, hData, &fi, &ei, &ai))
1908 return 0;
1909 if(!TrReadOperator(tr, "]"))
1910 return 0;
1911 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1913 if(azd->mIrs[0] != nullptr)
1915 TrErrorAt(tr, line, col, "Redefinition of source.\n");
1916 return 0;
1918 if(!TrReadOperator(tr, "="))
1919 return 0;
1921 for(;;)
1923 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, hData->mIrPoints, hrir.get()))
1936 return 0;
1938 uint ti{0};
1939 if(hData->mChannelType == CT_STEREO)
1941 char ident[MAX_IDENT_LEN+1];
1943 if(!TrReadIdent(tr, MAX_IDENT_LEN, ident))
1944 return 0;
1945 ti = static_cast<uint>(MatchTargetEar(ident));
1946 if(static_cast<int>(ti) < 0)
1948 TrErrorAt(tr, line, col, "Expected a target ear.\n");
1949 return 0;
1952 azd->mIrs[ti] = &hrirs[hData->mIrSize * (ti * hData->mIrCount + azd->mIndex)];
1953 azd->mDelays[ti] = AverageHrirOnset(onsetResampler, onsetSamples, hData->mIrRate,
1954 hData->mIrPoints, hrir.get(), 1.0 / factor[ti], azd->mDelays[ti]);
1955 if(resampler)
1956 resampler->process(hData->mIrPoints, hrir.get(), hData->mIrSize, hrir.get());
1957 AverageHrirMagnitude(irPoints, hData->mFftSize, hrir.get(), 1.0 / factor[ti],
1958 azd->mIrs[ti]);
1959 factor[ti] += 1.0;
1960 if(!TrIsOperator(tr, "+"))
1961 break;
1962 TrReadOperator(tr, "+");
1964 if(hData->mChannelType == CT_STEREO)
1966 if(azd->mIrs[0] == nullptr)
1968 TrErrorAt(tr, line, col, "Missing left ear source reference(s).\n");
1969 return 0;
1971 else if(azd->mIrs[1] == nullptr)
1973 TrErrorAt(tr, line, col, "Missing right ear source reference(s).\n");
1974 return 0;
1978 printf("\n");
1979 hrir = nullptr;
1980 if(resampler)
1982 hData->mIrRate = outRate;
1983 hData->mIrPoints = irPoints;
1984 resampler.reset();
1986 for(fi = 0;fi < hData->mFds.size();fi++)
1988 for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
1990 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
1992 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1993 if(azd->mIrs[0] != nullptr)
1994 break;
1996 if(ai < hData->mFds[fi].mEvs[ei].mAzs.size())
1997 break;
1999 if(ei >= hData->mFds[fi].mEvs.size())
2001 TrError(tr, "Missing source references [ %d, *, * ].\n", fi);
2002 return 0;
2004 hData->mFds[fi].mEvStart = ei;
2005 for(;ei < hData->mFds[fi].mEvs.size();ei++)
2007 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
2009 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
2011 if(azd->mIrs[0] == nullptr)
2013 TrError(tr, "Missing source reference [ %d, %d, %d ].\n", fi, ei, ai);
2014 return 0;
2019 for(uint ti{0};ti < channels;ti++)
2021 for(fi = 0;fi < hData->mFds.size();fi++)
2023 for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
2025 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
2027 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
2029 azd->mIrs[ti] = &hrirs[hData->mIrSize * (ti * hData->mIrCount + azd->mIndex)];
2034 if(!TrLoad(tr))
2036 mysofa_cache_release_all();
2037 return 1;
2040 TrError(tr, "Errant data at end of source list.\n");
2041 mysofa_cache_release_all();
2042 return 0;
2046 bool LoadDefInput(std::istream &istream, const char *startbytes, std::streamsize startbytecount,
2047 const char *filename, const uint fftSize, const uint truncSize, const uint outRate,
2048 const ChannelModeT chanMode, HrirDataT *hData)
2050 TokenReaderT tr{istream};
2052 TrSetup(startbytes, startbytecount, filename, &tr);
2053 if(!ProcessMetrics(&tr, fftSize, truncSize, chanMode, hData)
2054 || !ProcessSources(&tr, hData, outRate))
2055 return false;
2057 return true;