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
40 #include <string_view>
45 #include "alnumeric.h"
49 #include "polyphase_resampler.h"
50 #include "sofa-support.h"
56 // Constants for accessing the token reader's ring buffer.
57 constexpr uint TRRingBits
{16};
58 constexpr uint TRRingSize
{1 << TRRingBits
};
59 constexpr uint TRRingMask
{TRRingSize
- 1};
61 // The token reader's load interval in bytes.
62 constexpr uint TRLoadSize
{TRRingSize
>> 2};
64 // Token reader state for parsing the data set definition.
66 std::istream
&mIStream
;
70 std::array
<char,TRRingSize
> mRing
{};
71 std::streamsize mIn
{};
72 std::streamsize mOut
{};
74 TokenReaderT(std::istream
&istream
) noexcept
: mIStream
{istream
} { }
75 TokenReaderT(const TokenReaderT
&) = default;
79 // The maximum identifier length used when processing the data set
81 constexpr uint MaxIdentLen
{16};
83 // The limits for the listener's head 'radius' in the data set definition.
84 constexpr double MinRadius
{0.05};
85 constexpr double MaxRadius
{0.15};
87 // The maximum number of channels that can be addressed for a WAVE file
88 // source listed in the data set definition.
89 constexpr uint MaxWaveChannels
{65535};
91 // The limits to the byte size for a binary source listed in the definition
98 // The limits to the number of significant bits for an ASCII source listed in
99 // the data set definition.
105 // The four-character-codes for RIFF/RIFX WAVE file chunks.
107 FOURCC_RIFF
= 0x46464952, // 'RIFF'
108 FOURCC_RIFX
= 0x58464952, // 'RIFX'
109 FOURCC_WAVE
= 0x45564157, // 'WAVE'
110 FOURCC_FMT
= 0x20746D66, // 'fmt '
111 FOURCC_DATA
= 0x61746164, // 'data'
112 FOURCC_LIST
= 0x5453494C, // 'LIST'
113 FOURCC_WAVL
= 0x6C766177, // 'wavl'
114 FOURCC_SLNT
= 0x746E6C73, // 'slnt'
117 // The supported wave formats.
119 WAVE_FORMAT_PCM
= 0x0001,
120 WAVE_FORMAT_IEEE_FLOAT
= 0x0003,
121 WAVE_FORMAT_EXTENSIBLE
= 0xFFFE,
131 // Source format for the references listed in the data set definition.
134 SF_ASCII
, // ASCII text file.
135 SF_BIN_LE
, // Little-endian binary file.
136 SF_BIN_BE
, // Big-endian binary file.
137 SF_WAVE
, // RIFF/RIFX WAVE file.
138 SF_SOFA
// Spatially Oriented Format for Accoustics (SOFA) file.
141 // Element types for the references listed in the data set definition.
144 ET_INT
, // Integer elements.
145 ET_FP
// Floating-point elements.
148 // Source reference state used when loading sources.
150 SourceFormatT mFormat
;
160 std::array
<char,MAX_PATH_LEN
+1> mPath
;
164 /* Whitespace is not significant. It can process tokens as identifiers, numbers
165 * (integer and floating-point), strings, and operators. Strings must be
166 * encapsulated by double-quotes and cannot span multiple lines.
169 // Setup the reader on the given file. The filename can be NULL if no error
170 // output is desired.
171 void TrSetup(const al::span
<const char> startbytes
, const std::string_view filename
,
174 std::string_view namepart
;
176 if(!filename
.empty())
178 const auto fslashpos
= filename
.rfind('/');
179 const auto bslashpos
= filename
.rfind('\\');
180 const auto slashpos
= (bslashpos
>= filename
.size()) ? fslashpos
:
181 (fslashpos
>= filename
.size()) ? bslashpos
:
182 std::max(fslashpos
, bslashpos
);
183 if(slashpos
< filename
.size())
184 namepart
= filename
.substr(slashpos
+1);
187 tr
->mName
= namepart
;
193 if(!startbytes
.empty())
195 assert(startbytes
.size() <= tr
->mRing
.size());
196 std::copy(startbytes
.cbegin(), startbytes
.cend(), tr
->mRing
.begin());
197 tr
->mIn
+= std::streamsize(startbytes
.size());
201 // Prime the reader's ring buffer, and return a result indicating that there
202 // is text to process.
203 auto TrLoad(TokenReaderT
*tr
) -> int
205 std::istream
&istream
= tr
->mIStream
;
207 std::streamsize toLoad
{TRRingSize
- static_cast<std::streamsize
>(tr
->mIn
- tr
->mOut
)};
208 if(toLoad
>= TRLoadSize
&& istream
.good())
210 // Load TRLoadSize (or less if at the end of the file) per read.
213 const auto in
= tr
->mIn
&TRRingMask
;
214 std::streamsize count
{TRRingSize
- in
};
217 istream
.read(al::to_address(tr
->mRing
.begin() + in
), count
);
218 tr
->mIn
+= istream
.gcount();
219 istream
.read(tr
->mRing
.data(), toLoad
-count
);
220 tr
->mIn
+= istream
.gcount();
224 istream
.read(al::to_address(tr
->mRing
.begin() + in
), toLoad
);
225 tr
->mIn
+= istream
.gcount();
228 if(tr
->mOut
>= TRRingSize
)
230 tr
->mOut
-= TRRingSize
;
231 tr
->mIn
-= TRRingSize
;
234 if(tr
->mIn
> tr
->mOut
)
239 // Error display routine. Only displays when the base name is not NULL.
240 void TrErrorVA(const TokenReaderT
*tr
, uint line
, uint column
, const char *format
, va_list argPtr
)
242 if(tr
->mName
.empty())
244 fprintf(stderr
, "\nError (%s:%u:%u): ", tr
->mName
.c_str(), line
, column
);
245 vfprintf(stderr
, format
, argPtr
);
248 // Used to display an error at a saved line/column.
249 void TrErrorAt(const TokenReaderT
*tr
, uint line
, uint column
, const char *format
, ...)
251 /* NOLINTBEGIN(*-array-to-pointer-decay) */
253 va_start(argPtr
, format
);
254 TrErrorVA(tr
, line
, column
, format
, argPtr
);
256 /* NOLINTEND(*-array-to-pointer-decay) */
259 // Used to display an error at the current line/column.
260 void TrError(const TokenReaderT
*tr
, const char *format
, ...)
262 /* NOLINTBEGIN(*-array-to-pointer-decay) */
264 va_start(argPtr
, format
);
265 TrErrorVA(tr
, tr
->mLine
, tr
->mColumn
, format
, argPtr
);
267 /* NOLINTEND(*-array-to-pointer-decay) */
270 // Skips to the next line.
271 void TrSkipLine(TokenReaderT
*tr
)
277 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
289 // Skips to the next token.
290 auto TrSkipWhitespace(TokenReaderT
*tr
) -> int
294 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
314 // Get the line and/or column of the next token (or the end of input).
315 void TrIndication(TokenReaderT
*tr
, uint
*line
, uint
*column
)
317 TrSkipWhitespace(tr
);
318 if(line
) *line
= tr
->mLine
;
319 if(column
) *column
= tr
->mColumn
;
322 // Checks to see if a token is (likely to be) an identifier. It does not
323 // display any errors and will not proceed to the next token.
324 auto TrIsIdent(TokenReaderT
*tr
) -> int
326 if(!TrSkipWhitespace(tr
))
328 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
329 return ch
== '_' || isalpha(ch
);
333 // Checks to see if a token is the given operator. It does not display any
334 // errors and will not proceed to the next token.
335 auto TrIsOperator(TokenReaderT
*tr
, const std::string_view op
) -> int
337 if(!TrSkipWhitespace(tr
))
341 while(len
< op
.size() && out
< tr
->mIn
)
343 if(tr
->mRing
[out
&TRRingMask
] != op
[len
])
353 /* The TrRead*() routines obtain the value of a matching token type. They
354 * display type, form, and boundary errors and will proceed to the next
358 // Reads and validates an identifier token.
359 auto TrReadIdent(TokenReaderT
*tr
, const al::span
<char> ident
) -> int
361 assert(!ident
.empty());
362 const size_t maxLen
{ident
.size()-1};
363 uint col
{tr
->mColumn
};
364 if(TrSkipWhitespace(tr
))
367 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
368 if(ch
== '_' || isalpha(ch
))
378 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
379 } while(ch
== '_' || isdigit(ch
) || isalpha(ch
));
381 tr
->mColumn
+= static_cast<uint
>(len
);
387 TrErrorAt(tr
, tr
->mLine
, col
, "Identifier is too long.\n");
391 TrErrorAt(tr
, tr
->mLine
, col
, "Expected an identifier.\n");
395 // Reads and validates (including bounds) an integer token.
396 auto TrReadInt(TokenReaderT
*tr
, const int loBound
, const int hiBound
, int *value
) -> int
398 uint col
{tr
->mColumn
};
399 if(TrSkipWhitespace(tr
))
403 std::array
<char,64+1> temp
{};
404 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
405 if(ch
== '+' || ch
== '-')
414 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
415 if(!isdigit(ch
)) break;
423 if(digis
> 0 && ch
!= '.' && !isalpha(ch
))
427 TrErrorAt(tr
, tr
->mLine
, col
, "Integer is too long.");
431 *value
= static_cast<int>(strtol(temp
.data(), nullptr, 10));
432 if(*value
< loBound
|| *value
> hiBound
)
434 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a value from %d to %d.\n", loBound
, hiBound
);
440 TrErrorAt(tr
, tr
->mLine
, col
, "Expected an integer.\n");
444 // Reads and validates (including bounds) a float token.
445 auto TrReadFloat(TokenReaderT
*tr
, const double loBound
, const double hiBound
, double *value
) -> int
447 uint col
{tr
->mColumn
};
448 if(TrSkipWhitespace(tr
))
451 std::array
<char,64+1> temp
{};
453 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
454 if(ch
== '+' || ch
== '-')
464 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
465 if(!isdigit(ch
)) break;
481 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
482 if(!isdigit(ch
)) break;
491 if(ch
== 'E' || ch
== 'e')
498 if(ch
== '+' || ch
== '-')
507 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
508 if(!isdigit(ch
)) break;
517 if(digis
> 0 && ch
!= '.' && !isalpha(ch
))
521 TrErrorAt(tr
, tr
->mLine
, col
, "Float is too long.");
525 *value
= strtod(temp
.data(), nullptr);
526 if(*value
< loBound
|| *value
> hiBound
)
528 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a value from %f to %f.\n", loBound
, hiBound
);
537 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a float.\n");
541 // Reads and validates a string token.
542 auto TrReadString(TokenReaderT
*tr
, const al::span
<char> text
) -> int
544 assert(!text
.empty());
545 const size_t maxLen
{text
.size()-1};
547 uint col
{tr
->mColumn
};
548 if(TrSkipWhitespace(tr
))
551 if(char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]}; ch
== '\"')
557 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
563 TrErrorAt(tr
, tr
->mLine
, col
, "Unterminated string at end of line.\n");
572 tr
->mColumn
+= static_cast<uint
>(1 + len
);
573 TrErrorAt(tr
, tr
->mLine
, col
, "Unterminated string at end of input.\n");
576 tr
->mColumn
+= static_cast<uint
>(2 + len
);
579 TrErrorAt(tr
, tr
->mLine
, col
, "String is too long.\n");
586 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a string.\n");
590 // Reads and validates the given operator.
591 auto TrReadOperator(TokenReaderT
*tr
, const std::string_view op
) -> int
593 uint col
{tr
->mColumn
};
594 if(TrSkipWhitespace(tr
))
598 while(len
< op
.size() && TrLoad(tr
))
600 if(tr
->mRing
[tr
->mOut
&TRRingMask
] != op
[len
])
605 tr
->mColumn
+= static_cast<uint
>(len
);
609 TrErrorAt(tr
, tr
->mLine
, col
, "Expected '%s' operator.\n", op
);
614 /*************************
615 *** File source input ***
616 *************************/
618 // Read a binary value of the specified byte order and byte size from a file,
619 // storing it as a 32-bit unsigned integer.
620 auto ReadBin4(std::istream
&istream
, const char *filename
, const ByteOrderT order
,
621 const uint bytes
, uint32_t *out
) -> int
623 std::array
<uint8_t,4> in
{};
624 istream
.read(reinterpret_cast<char*>(in
.data()), static_cast<int>(bytes
));
625 if(istream
.gcount() != bytes
)
627 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
634 for(uint i
= 0;i
< bytes
;i
++)
635 accum
= (accum
<<8) | in
[bytes
- i
- 1];
638 for(uint i
= 0;i
< bytes
;i
++)
639 accum
= (accum
<<8) | in
[i
];
648 // Read a binary value of the specified byte order from a file, storing it as
649 // a 64-bit unsigned integer.
650 auto ReadBin8(std::istream
&istream
, const char *filename
, const ByteOrderT order
, uint64_t *out
) -> int
652 std::array
<uint8_t,8> in
{};
653 istream
.read(reinterpret_cast<char*>(in
.data()), 8);
654 if(istream
.gcount() != 8)
656 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
664 for(uint i
{0};i
< 8;++i
)
665 accum
= (accum
<<8) | in
[8 - i
- 1];
668 for(uint i
{0};i
< 8;++i
)
669 accum
= (accum
<<8) | in
[i
];
678 /* Read a binary value of the specified type, byte order, and byte size from
679 * a file, converting it to a double. For integer types, the significant
680 * bits are used to normalize the result. The sign of bits determines
681 * whether they are padded toward the MSB (negative) or LSB (positive).
682 * Floating-point types are not normalized.
684 auto ReadBinAsDouble(std::istream
&istream
, const char *filename
, const ByteOrderT order
,
685 const ElementTypeT type
, const uint bytes
, const int bits
, double *out
) -> int
691 if(!ReadBin8(istream
, filename
, order
, &val
))
694 *out
= al::bit_cast
<double>(val
);
699 if(!ReadBin4(istream
, filename
, order
, bytes
, &val
))
702 *out
= al::bit_cast
<float>(val
);
706 val
>>= (8*bytes
) - (static_cast<uint
>(bits
));
708 val
&= (0xFFFFFFFF >> (32+bits
));
710 if(val
&static_cast<uint
>(1<<(std::abs(bits
)-1)))
711 val
|= (0xFFFFFFFF << std::abs(bits
));
712 *out
= static_cast<int32_t>(val
) / static_cast<double>(1<<(std::abs(bits
)-1));
718 /* Read an ascii value of the specified type from a file, converting it to a
719 * double. For integer types, the significant bits are used to normalize the
720 * result. The sign of the bits should always be positive. This also skips
721 * up to one separator character before the element itself.
723 auto ReadAsciiAsDouble(TokenReaderT
*tr
, const char *filename
, const ElementTypeT type
,
724 const uint bits
, double *out
) -> int
726 if(TrIsOperator(tr
, ","))
727 TrReadOperator(tr
, ",");
728 else if(TrIsOperator(tr
, ":"))
729 TrReadOperator(tr
, ":");
730 else if(TrIsOperator(tr
, ";"))
731 TrReadOperator(tr
, ";");
732 else if(TrIsOperator(tr
, "|"))
733 TrReadOperator(tr
, "|");
737 if(!TrReadFloat(tr
, -std::numeric_limits
<double>::infinity(),
738 std::numeric_limits
<double>::infinity(), out
))
740 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
747 if(!TrReadInt(tr
, -(1<<(bits
-1)), (1<<(bits
-1))-1, &v
))
749 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
752 *out
= v
/ static_cast<double>((1<<(bits
-1))-1);
757 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
758 // the source parameters and data set metrics.
759 auto ReadWaveFormat(std::istream
&istream
, const ByteOrderT order
, const uint hrirRate
,
760 SourceRefT
*src
) -> int
762 uint32_t fourCC
, chunkSize
;
763 uint32_t format
, channels
, rate
, dummy
, block
, size
, bits
;
768 istream
.seekg(static_cast<int>(chunkSize
), std::ios::cur
);
769 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
770 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
772 } while(fourCC
!= FOURCC_FMT
);
773 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &format
)
774 || !ReadBin4(istream
, src
->mPath
.data(), order
, 2, &channels
)
775 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &rate
)
776 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &dummy
)
777 || !ReadBin4(istream
, src
->mPath
.data(), order
, 2, &block
))
782 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &size
))
790 if(format
== WAVE_FORMAT_EXTENSIBLE
)
792 istream
.seekg(2, std::ios::cur
);
793 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &bits
))
797 istream
.seekg(4, std::ios::cur
);
798 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &format
))
800 istream
.seekg(static_cast<int>(chunkSize
- 26), std::ios::cur
);
806 istream
.seekg(static_cast<int>(chunkSize
- 16), std::ios::cur
);
808 istream
.seekg(static_cast<int>(chunkSize
- 14), std::ios::cur
);
810 if(format
!= WAVE_FORMAT_PCM
&& format
!= WAVE_FORMAT_IEEE_FLOAT
)
812 fprintf(stderr
, "\nError: Unsupported WAVE format in file '%s'.\n", src
->mPath
.data());
815 if(src
->mChannel
>= channels
)
817 fprintf(stderr
, "\nError: Missing source channel in WAVE file '%s'.\n", src
->mPath
.data());
822 fprintf(stderr
, "\nError: Mismatched source sample rate in WAVE file '%s'.\n",
826 if(format
== WAVE_FORMAT_PCM
)
828 if(size
< 2 || size
> 4)
830 fprintf(stderr
, "\nError: Unsupported sample size in WAVE file '%s'.\n",
834 if(bits
< 16 || bits
> (8*size
))
836 fprintf(stderr
, "\nError: Bad significant bits in WAVE file '%s'.\n",
844 if(size
!= 4 && size
!= 8)
846 fprintf(stderr
, "\nError: Unsupported sample size in WAVE file '%s'.\n",
853 src
->mBits
= static_cast<int>(bits
);
854 src
->mSkip
= channels
;
858 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
859 auto ReadWaveData(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
860 const al::span
<double> hrir
) -> int
862 auto pre
= static_cast<int>(src
->mSize
* src
->mChannel
);
863 auto post
= static_cast<int>(src
->mSize
* (src
->mSkip
- src
->mChannel
- 1));
865 for(size_t i
{0};i
< hrir
.size();++i
)
869 istream
.seekg(skip
, std::ios::cur
);
870 if(!ReadBinAsDouble(istream
, src
->mPath
.data(), order
, src
->mType
, src
->mSize
, src
->mBits
,
876 istream
.seekg(skip
, std::ios::cur
);
880 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
882 auto ReadWaveList(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
883 const al::span
<double> hrir
) -> int
885 uint32_t fourCC
, chunkSize
, listSize
, count
;
886 uint block
, skip
, offset
, i
;
891 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
892 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
895 if(fourCC
== FOURCC_DATA
)
897 block
= src
->mSize
* src
->mSkip
;
898 count
= chunkSize
/ block
;
899 if(count
< (src
->mOffset
+ hrir
.size()))
901 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", src
->mPath
.data());
904 using off_type
= std::istream::off_type
;
905 istream
.seekg(off_type(src
->mOffset
) * off_type(block
), std::ios::cur
);
906 if(!ReadWaveData(istream
, src
, order
, hrir
))
910 if(fourCC
== FOURCC_LIST
)
912 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
))
915 if(fourCC
== FOURCC_WAVL
)
919 istream
.seekg(static_cast<long>(chunkSize
), std::ios::cur
);
921 listSize
= chunkSize
;
922 block
= src
->mSize
* src
->mSkip
;
926 while(offset
< hrir
.size() && listSize
> 8)
928 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
929 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
931 listSize
-= 8 + chunkSize
;
932 if(fourCC
== FOURCC_DATA
)
934 count
= chunkSize
/ block
;
937 using off_type
= std::istream::off_type
;
938 istream
.seekg(off_type(skip
) * off_type(block
), std::ios::cur
);
939 chunkSize
-= skip
* block
;
942 if(count
> (hrir
.size() - offset
))
943 count
= static_cast<uint
>(hrir
.size() - offset
);
944 if(!ReadWaveData(istream
, src
, order
, hrir
.subspan(offset
, count
)))
946 chunkSize
-= count
* block
;
948 lastSample
= hrir
[offset
- 1];
956 else if(fourCC
== FOURCC_SLNT
)
958 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 4, &count
))
965 if(count
> (hrir
.size() - offset
))
966 count
= static_cast<uint
>(hrir
.size() - offset
);
967 for(i
= 0; i
< count
; i
++)
968 hrir
[offset
+ i
] = lastSample
;
978 istream
.seekg(static_cast<long>(chunkSize
), std::ios::cur
);
980 if(offset
< hrir
.size())
982 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", src
->mPath
.data());
988 // Load a source HRIR from an ASCII text file containing a list of elements
989 // separated by whitespace or common list operators (',', ';', ':', '|').
990 auto LoadAsciiSource(std::istream
&istream
, const SourceRefT
*src
, const al::span
<double> hrir
) -> int
992 TokenReaderT tr
{istream
};
994 TrSetup({}, {}, &tr
);
995 for(uint i
{0};i
< src
->mOffset
;++i
)
998 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
, static_cast<uint
>(src
->mBits
),
1002 for(size_t i
{0};i
< hrir
.size();++i
)
1004 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
, static_cast<uint
>(src
->mBits
),
1007 for(uint j
{0};j
< src
->mSkip
;++j
)
1010 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
,
1011 static_cast<uint
>(src
->mBits
), &dummy
))
1018 // Load a source HRIR from a binary file.
1019 auto LoadBinarySource(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
1020 const al::span
<double> hrir
) -> int
1022 istream
.seekg(static_cast<long>(src
->mOffset
), std::ios::beg
);
1023 for(size_t i
{0};i
< hrir
.size();++i
)
1025 if(!ReadBinAsDouble(istream
, src
->mPath
.data(), order
, src
->mType
, src
->mSize
, src
->mBits
,
1029 istream
.seekg(static_cast<long>(src
->mSkip
), std::ios::cur
);
1034 // Load a source HRIR from a RIFF/RIFX WAVE file.
1035 auto LoadWaveSource(std::istream
&istream
, SourceRefT
*src
, const uint hrirRate
,
1036 const al::span
<double> hrir
) -> int
1038 uint32_t fourCC
, dummy
;
1041 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
1042 || !ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &dummy
))
1044 if(fourCC
== FOURCC_RIFF
)
1046 else if(fourCC
== FOURCC_RIFX
)
1050 fprintf(stderr
, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src
->mPath
.data());
1054 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
))
1056 if(fourCC
!= FOURCC_WAVE
)
1058 fprintf(stderr
, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src
->mPath
.data());
1061 if(!ReadWaveFormat(istream
, order
, hrirRate
, src
))
1063 if(!ReadWaveList(istream
, src
, order
, hrir
))
1069 struct SofaEasyDeleter
{
1070 void operator()(gsl::owner
<MYSOFA_EASY
*> sofa
)
1072 if(sofa
->neighborhood
) mysofa_neighborhood_free(sofa
->neighborhood
);
1073 if(sofa
->lookup
) mysofa_lookup_free(sofa
->lookup
);
1074 if(sofa
->hrtf
) mysofa_free(sofa
->hrtf
);
1078 using SofaEasyPtr
= std::unique_ptr
<MYSOFA_EASY
,SofaEasyDeleter
>;
1080 struct SofaCacheEntry
{
1085 std::vector
<SofaCacheEntry
> gSofaCache
;
1087 // Load a Spatially Oriented Format for Accoustics (SOFA) file.
1088 auto LoadSofaFile(SourceRefT
*src
, const uint hrirRate
, const uint n
) -> MYSOFA_EASY
*
1090 const std::string_view srcname
{src
->mPath
.data()};
1091 auto iter
= std::find_if(gSofaCache
.begin(), gSofaCache
.end(),
1092 [srcname
,hrirRate
](SofaCacheEntry
&entry
) -> bool
1093 { return entry
.mName
== srcname
&& entry
.mSampleRate
== hrirRate
; });
1094 if(iter
!= gSofaCache
.end()) return iter
->mSofa
.get();
1096 SofaEasyPtr sofa
{new(std::nothrow
) MYSOFA_EASY
{}};
1099 fprintf(stderr
, "\nError: Out of memory.\n");
1102 sofa
->lookup
= nullptr;
1103 sofa
->neighborhood
= nullptr;
1106 sofa
->hrtf
= mysofa_load(src
->mPath
.data(), &err
);
1109 fprintf(stderr
, "\nError: Could not load source file '%s': %s (%d).\n",
1110 src
->mPath
.data(), SofaErrorStr(err
), err
);
1113 /* NOTE: Some valid SOFA files are failing this check. */
1114 err
= mysofa_check(sofa
->hrtf
);
1115 if(err
!= MYSOFA_OK
)
1116 fprintf(stderr
, "\nWarning: Supposedly malformed source file '%s': %s (%d).\n",
1117 src
->mPath
.data(), SofaErrorStr(err
), err
);
1118 if((src
->mOffset
+ n
) > sofa
->hrtf
->N
)
1120 fprintf(stderr
, "\nError: Not enough samples in SOFA file '%s'.\n", src
->mPath
.data());
1123 if(src
->mChannel
>= sofa
->hrtf
->R
)
1125 fprintf(stderr
, "\nError: Missing source receiver in SOFA file '%s'.\n",src
->mPath
.data());
1128 mysofa_tocartesian(sofa
->hrtf
);
1129 sofa
->lookup
= mysofa_lookup_init(sofa
->hrtf
);
1130 if(sofa
->lookup
== nullptr)
1132 fprintf(stderr
, "\nError: Out of memory.\n");
1135 gSofaCache
.emplace_back(SofaCacheEntry
{std::string
{srcname
}, hrirRate
, std::move(sofa
)});
1136 return gSofaCache
.back().mSofa
.get();
1139 // Copies the HRIR data from a particular SOFA measurement.
1140 void ExtractSofaHrir(const MYSOFA_HRTF
*hrtf
, const size_t index
, const size_t channel
,
1141 const size_t offset
, const al::span
<double> hrir
)
1143 const auto irValues
= al::span
{hrtf
->DataIR
.values
, hrtf
->DataIR
.elements
}
1144 .subspan((index
*hrtf
->R
+ channel
)*hrtf
->N
+ offset
);
1145 std::copy_n(irValues
.cbegin(), hrir
.size(), hrir
.begin());
1148 // Load a source HRIR from a Spatially Oriented Format for Accoustics (SOFA)
1150 auto LoadSofaSource(SourceRefT
*src
, const uint hrirRate
, const al::span
<double> hrir
) -> int
1152 MYSOFA_EASY
*sofa
{LoadSofaFile(src
, hrirRate
, static_cast<uint
>(hrir
.size()))};
1153 if(sofa
== nullptr) return 0;
1155 /* NOTE: At some point it may be beneficial or necessary to consider the
1156 various coordinate systems, listener/source orientations, and
1157 directional vectors defined in the SOFA file.
1160 static_cast<float>(src
->mAzimuth
),
1161 static_cast<float>(src
->mElevation
),
1162 static_cast<float>(src
->mRadius
)
1164 mysofa_s2c(target
.data());
1166 int nearest
{mysofa_lookup(sofa
->lookup
, target
.data())};
1169 fprintf(stderr
, "\nError: Lookup failed in source file '%s'.\n", src
->mPath
.data());
1173 al::span
<float,3> coords
= al::span
{sofa
->hrtf
->SourcePosition
.values
, sofa
->hrtf
->M
*3_uz
}
1174 .subspan(static_cast<uint
>(nearest
)*3_uz
).first
<3>();
1175 if(std::abs(coords
[0] - target
[0]) > 0.001 || std::abs(coords
[1] - target
[1]) > 0.001
1176 || std::abs(coords
[2] - target
[2]) > 0.001)
1178 fprintf(stderr
, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n",
1179 src
->mRadius
, src
->mElevation
, src
->mAzimuth
, src
->mPath
.data());
1180 target
[0] = coords
[0];
1181 target
[1] = coords
[1];
1182 target
[2] = coords
[2];
1183 mysofa_c2s(target
.data());
1184 fprintf(stderr
, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target
[2],
1185 target
[1], target
[0]);
1189 ExtractSofaHrir(sofa
->hrtf
, static_cast<uint
>(nearest
), src
->mChannel
, src
->mOffset
, hrir
);
1194 // Load a source HRIR from a supported file type.
1195 auto LoadSource(SourceRefT
*src
, const uint hrirRate
, const al::span
<double> hrir
) -> int
1197 std::unique_ptr
<std::istream
> istream
;
1198 if(src
->mFormat
!= SF_SOFA
)
1200 if(src
->mFormat
== SF_ASCII
)
1201 istream
= std::make_unique
<std::ifstream
>(std::filesystem::u8path(src
->mPath
.data()));
1203 istream
= std::make_unique
<std::ifstream
>(std::filesystem::u8path(src
->mPath
.data()),
1205 if(!istream
->good())
1207 fprintf(stderr
, "\nError: Could not open source file '%s'.\n", src
->mPath
.data());
1212 switch(src
->mFormat
)
1214 case SF_ASCII
: return LoadAsciiSource(*istream
, src
, hrir
);
1215 case SF_BIN_LE
: return LoadBinarySource(*istream
, src
, BO_LITTLE
, hrir
);
1216 case SF_BIN_BE
: return LoadBinarySource(*istream
, src
, BO_BIG
, hrir
);
1217 case SF_WAVE
: return LoadWaveSource(*istream
, src
, hrirRate
, hrir
);
1218 case SF_SOFA
: return LoadSofaSource(src
, hrirRate
, hrir
);
1219 case SF_NONE
: break;
1225 // Match the channel type from a given identifier.
1226 auto MatchChannelType(const char *ident
) -> ChannelTypeT
1228 if(al::strcasecmp(ident
, "mono") == 0)
1230 if(al::strcasecmp(ident
, "stereo") == 0)
1236 // Process the data set definition to read and validate the data set metrics.
1237 auto ProcessMetrics(TokenReaderT
*tr
, const uint fftSize
, const uint truncSize
,
1238 const ChannelModeT chanMode
, HrirDataT
*hData
) -> int
1240 int hasRate
= 0, hasType
= 0, hasPoints
= 0, hasRadius
= 0;
1241 int hasDistance
= 0, hasAzimuths
= 0;
1242 std::array
<char,MaxIdentLen
+1> ident
{};
1247 std::array
<double,MAX_FD_COUNT
> distances
{};
1249 std::array
<uint
,MAX_FD_COUNT
> evCounts
{};
1250 auto azCounts
= std::vector
<std::array
<uint
,MAX_EV_COUNT
>>(MAX_FD_COUNT
);
1251 for(auto &azs
: azCounts
) azs
.fill(0u);
1253 TrIndication(tr
, &line
, &col
);
1254 while(TrIsIdent(tr
))
1256 TrIndication(tr
, &line
, &col
);
1257 if(!TrReadIdent(tr
, ident
))
1259 if(al::strcasecmp(ident
.data(), "rate") == 0)
1263 TrErrorAt(tr
, line
, col
, "Redefinition of 'rate'.\n");
1266 if(!TrReadOperator(tr
, "="))
1268 if(!TrReadInt(tr
, MIN_RATE
, MAX_RATE
, &intVal
))
1270 hData
->mIrRate
= static_cast<uint
>(intVal
);
1273 else if(al::strcasecmp(ident
.data(), "type") == 0)
1275 std::array
<char,MaxIdentLen
+1> type
{};
1279 TrErrorAt(tr
, line
, col
, "Redefinition of 'type'.\n");
1282 if(!TrReadOperator(tr
, "="))
1285 if(!TrReadIdent(tr
, type
))
1287 hData
->mChannelType
= MatchChannelType(type
.data());
1288 if(hData
->mChannelType
== CT_NONE
)
1290 TrErrorAt(tr
, line
, col
, "Expected a channel type.\n");
1293 if(hData
->mChannelType
== CT_STEREO
)
1295 if(chanMode
== CM_ForceMono
)
1296 hData
->mChannelType
= CT_MONO
;
1300 else if(al::strcasecmp(ident
.data(), "points") == 0)
1304 TrErrorAt(tr
, line
, col
, "Redefinition of 'points'.\n");
1307 if(!TrReadOperator(tr
, "="))
1309 TrIndication(tr
, &line
, &col
);
1310 if(!TrReadInt(tr
, MIN_POINTS
, MAX_POINTS
, &intVal
))
1312 points
= static_cast<uint
>(intVal
);
1313 if(fftSize
> 0 && points
> fftSize
)
1315 TrErrorAt(tr
, line
, col
, "Value exceeds the overridden FFT size.\n");
1318 if(points
< truncSize
)
1320 TrErrorAt(tr
, line
, col
, "Value is below the truncation size.\n");
1323 hData
->mIrPoints
= points
;
1324 hData
->mFftSize
= fftSize
;
1325 hData
->mIrSize
= 1 + (fftSize
/ 2);
1326 if(points
> hData
->mIrSize
)
1327 hData
->mIrSize
= points
;
1330 else if(al::strcasecmp(ident
.data(), "radius") == 0)
1334 TrErrorAt(tr
, line
, col
, "Redefinition of 'radius'.\n");
1337 if(!TrReadOperator(tr
, "="))
1339 if(!TrReadFloat(tr
, MinRadius
, MaxRadius
, &fpVal
))
1341 hData
->mRadius
= fpVal
;
1344 else if(al::strcasecmp(ident
.data(), "distance") == 0)
1350 TrErrorAt(tr
, line
, col
, "Redefinition of 'distance'.\n");
1353 if(!TrReadOperator(tr
, "="))
1358 if(!TrReadFloat(tr
, MIN_DISTANCE
, MAX_DISTANCE
, &fpVal
))
1360 if(count
> 0 && fpVal
<= distances
[count
- 1])
1362 TrError(tr
, "Distances are not ascending.\n");
1365 distances
[count
++] = fpVal
;
1366 if(!TrIsOperator(tr
, ","))
1368 if(count
>= MAX_FD_COUNT
)
1370 TrError(tr
, "Exceeded the maximum of %d fields.\n", MAX_FD_COUNT
);
1373 TrReadOperator(tr
, ",");
1375 if(fdCount
!= 0 && count
!= fdCount
)
1377 TrError(tr
, "Did not match the specified number of %d fields.\n", fdCount
);
1383 else if(al::strcasecmp(ident
.data(), "azimuths") == 0)
1389 TrErrorAt(tr
, line
, col
, "Redefinition of 'azimuths'.\n");
1392 if(!TrReadOperator(tr
, "="))
1398 if(!TrReadInt(tr
, MIN_AZ_COUNT
, MAX_AZ_COUNT
, &intVal
))
1400 azCounts
[count
][evCounts
[count
]++] = static_cast<uint
>(intVal
);
1401 if(TrIsOperator(tr
, ","))
1403 if(evCounts
[count
] >= MAX_EV_COUNT
)
1405 TrError(tr
, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT
);
1408 TrReadOperator(tr
, ",");
1412 if(evCounts
[count
] < MIN_EV_COUNT
)
1414 TrErrorAt(tr
, line
, col
, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT
);
1417 if(azCounts
[count
][0] != 1 || azCounts
[count
][evCounts
[count
] - 1] != 1)
1419 TrError(tr
, "Poles are not singular for field %d.\n", count
- 1);
1423 if(!TrIsOperator(tr
, ";"))
1426 if(count
>= MAX_FD_COUNT
)
1428 TrError(tr
, "Exceeded the maximum number of %d fields.\n", MAX_FD_COUNT
);
1431 evCounts
[count
] = 0;
1432 TrReadOperator(tr
, ";");
1435 if(fdCount
!= 0 && count
!= fdCount
)
1437 TrError(tr
, "Did not match the specified number of %d fields.\n", fdCount
);
1445 TrErrorAt(tr
, line
, col
, "Expected a metric name.\n");
1448 TrSkipWhitespace(tr
);
1450 if(!(hasRate
&& hasPoints
&& hasRadius
&& hasDistance
&& hasAzimuths
))
1452 TrErrorAt(tr
, line
, col
, "Expected a metric name.\n");
1455 if(distances
[0] < hData
->mRadius
)
1457 TrError(tr
, "Distance cannot start below head radius.\n");
1460 if(hData
->mChannelType
== CT_NONE
)
1461 hData
->mChannelType
= CT_MONO
;
1462 const auto azs
= al::span
{azCounts
}.first
<MAX_FD_COUNT
>();
1463 if(!PrepareHrirData(al::span
{distances
}.first(fdCount
), evCounts
, azs
, hData
))
1465 fprintf(stderr
, "Error: Out of memory.\n");
1471 // Parse an index triplet from the data set definition.
1472 auto ReadIndexTriplet(TokenReaderT
*tr
, const HrirDataT
*hData
, uint
*fi
, uint
*ei
, uint
*ai
)->int
1476 if(hData
->mFds
.size() > 1)
1478 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
.size()-1), &intVal
))
1480 *fi
= static_cast<uint
>(intVal
);
1481 if(!TrReadOperator(tr
, ","))
1488 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
[*fi
].mEvs
.size()-1), &intVal
))
1490 *ei
= static_cast<uint
>(intVal
);
1491 if(!TrReadOperator(tr
, ","))
1493 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
[*fi
].mEvs
[*ei
].mAzs
.size()-1), &intVal
))
1495 *ai
= static_cast<uint
>(intVal
);
1499 // Match the source format from a given identifier.
1500 auto MatchSourceFormat(const char *ident
) -> SourceFormatT
1502 if(al::strcasecmp(ident
, "ascii") == 0)
1504 if(al::strcasecmp(ident
, "bin_le") == 0)
1506 if(al::strcasecmp(ident
, "bin_be") == 0)
1508 if(al::strcasecmp(ident
, "wave") == 0)
1510 if(al::strcasecmp(ident
, "sofa") == 0)
1515 // Match the source element type from a given identifier.
1516 auto MatchElementType(const char *ident
) -> ElementTypeT
1518 if(al::strcasecmp(ident
, "int") == 0)
1520 if(al::strcasecmp(ident
, "fp") == 0)
1525 // Parse and validate a source reference from the data set definition.
1526 auto ReadSourceRef(TokenReaderT
*tr
, SourceRefT
*src
) -> int
1528 std::array
<char,MaxIdentLen
+1> ident
{};
1533 TrIndication(tr
, &line
, &col
);
1534 if(!TrReadIdent(tr
, ident
))
1536 src
->mFormat
= MatchSourceFormat(ident
.data());
1537 if(src
->mFormat
== SF_NONE
)
1539 TrErrorAt(tr
, line
, col
, "Expected a source format.\n");
1542 if(!TrReadOperator(tr
, "("))
1544 if(src
->mFormat
== SF_SOFA
)
1546 if(!TrReadFloat(tr
, MIN_DISTANCE
, MAX_DISTANCE
, &fpVal
))
1548 src
->mRadius
= fpVal
;
1549 if(!TrReadOperator(tr
, ","))
1551 if(!TrReadFloat(tr
, -90.0, 90.0, &fpVal
))
1553 src
->mElevation
= fpVal
;
1554 if(!TrReadOperator(tr
, ","))
1556 if(!TrReadFloat(tr
, -360.0, 360.0, &fpVal
))
1558 src
->mAzimuth
= fpVal
;
1559 if(!TrReadOperator(tr
, ":"))
1561 if(!TrReadInt(tr
, 0, MaxWaveChannels
, &intVal
))
1563 src
->mType
= ET_NONE
;
1566 src
->mChannel
= static_cast<uint
>(intVal
);
1569 else if(src
->mFormat
== SF_WAVE
)
1571 if(!TrReadInt(tr
, 0, MaxWaveChannels
, &intVal
))
1573 src
->mType
= ET_NONE
;
1576 src
->mChannel
= static_cast<uint
>(intVal
);
1581 TrIndication(tr
, &line
, &col
);
1582 if(!TrReadIdent(tr
, ident
))
1584 src
->mType
= MatchElementType(ident
.data());
1585 if(src
->mType
== ET_NONE
)
1587 TrErrorAt(tr
, line
, col
, "Expected a source element type.\n");
1590 if(src
->mFormat
== SF_BIN_LE
|| src
->mFormat
== SF_BIN_BE
)
1592 if(!TrReadOperator(tr
, ","))
1594 if(src
->mType
== ET_INT
)
1596 if(!TrReadInt(tr
, MinBinSize
, MaxBinSize
, &intVal
))
1598 src
->mSize
= static_cast<uint
>(intVal
);
1599 if(!TrIsOperator(tr
, ","))
1600 src
->mBits
= static_cast<int>(8*src
->mSize
);
1603 TrReadOperator(tr
, ",");
1604 TrIndication(tr
, &line
, &col
);
1605 if(!TrReadInt(tr
, -2147483647-1, 2147483647, &intVal
))
1607 if(std::abs(intVal
) < int{MinBinSize
}*8 || static_cast<uint
>(std::abs(intVal
)) > (8*src
->mSize
))
1609 TrErrorAt(tr
, line
, col
, "Expected a value of (+/-) %d to %d.\n", MinBinSize
*8, 8*src
->mSize
);
1612 src
->mBits
= intVal
;
1617 TrIndication(tr
, &line
, &col
);
1618 if(!TrReadInt(tr
, -2147483647-1, 2147483647, &intVal
))
1620 if(intVal
!= 4 && intVal
!= 8)
1622 TrErrorAt(tr
, line
, col
, "Expected a value of 4 or 8.\n");
1625 src
->mSize
= static_cast<uint
>(intVal
);
1629 else if(src
->mFormat
== SF_ASCII
&& src
->mType
== ET_INT
)
1631 if(!TrReadOperator(tr
, ","))
1633 if(!TrReadInt(tr
, MinASCIIBits
, MaxASCIIBits
, &intVal
))
1636 src
->mBits
= intVal
;
1644 if(!TrIsOperator(tr
, ";"))
1648 TrReadOperator(tr
, ";");
1649 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1651 src
->mSkip
= static_cast<uint
>(intVal
);
1654 if(!TrReadOperator(tr
, ")"))
1656 if(TrIsOperator(tr
, "@"))
1658 TrReadOperator(tr
, "@");
1659 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1661 src
->mOffset
= static_cast<uint
>(intVal
);
1665 if(!TrReadOperator(tr
, ":"))
1667 if(!TrReadString(tr
, src
->mPath
))
1672 // Parse and validate a SOFA source reference from the data set definition.
1673 auto ReadSofaRef(TokenReaderT
*tr
, SourceRefT
*src
) -> int
1675 std::array
<char,MaxIdentLen
+1> ident
{};
1679 TrIndication(tr
, &line
, &col
);
1680 if(!TrReadIdent(tr
, ident
))
1682 src
->mFormat
= MatchSourceFormat(ident
.data());
1683 if(src
->mFormat
!= SF_SOFA
)
1685 TrErrorAt(tr
, line
, col
, "Expected the SOFA source format.\n");
1689 src
->mType
= ET_NONE
;
1695 if(TrIsOperator(tr
, "@"))
1697 TrReadOperator(tr
, "@");
1698 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1700 src
->mOffset
= static_cast<uint
>(intVal
);
1704 if(!TrReadOperator(tr
, ":"))
1706 if(!TrReadString(tr
, src
->mPath
))
1711 // Match the target ear (index) from a given identifier.
1712 auto MatchTargetEar(const char *ident
) -> int
1714 if(al::strcasecmp(ident
, "left") == 0)
1716 if(al::strcasecmp(ident
, "right") == 0)
1721 // Calculate the onset time of an HRIR and average it with any existing
1722 // timing for its field, elevation, azimuth, and ear.
1723 constexpr int OnsetRateMultiple
{10};
1724 auto AverageHrirOnset(PPhaseResampler
&rs
, al::span
<double> upsampled
, const uint rate
,
1725 const al::span
<const double> hrir
, const double f
, const double onset
) -> double
1727 rs
.process(hrir
, upsampled
);
1729 auto abs_lt
= [](const double lhs
, const double rhs
) -> bool
1730 { return std::abs(lhs
) < std::abs(rhs
); };
1731 auto iter
= std::max_element(upsampled
.cbegin(), upsampled
.cend(), abs_lt
);
1732 return Lerp(onset
, static_cast<double>(std::distance(upsampled
.cbegin(), iter
))/(10*rate
), f
);
1735 // Calculate the magnitude response of an HRIR and average it with any
1736 // existing responses for its field, elevation, azimuth, and ear.
1737 void AverageHrirMagnitude(const uint fftSize
, const al::span
<const double> hrir
, const double f
,
1738 const al::span
<double> mag
)
1740 const uint m
{1 + (fftSize
/2)};
1741 std::vector
<complex_d
> h(fftSize
);
1742 std::vector
<double> r(m
);
1744 auto hiter
= std::copy(hrir
.cbegin(), hrir
.cend(), h
.begin());
1745 std::fill(hiter
, h
.end(), 0.0);
1747 MagnitudeResponse(h
, r
);
1748 for(uint i
{0};i
< m
;++i
)
1749 mag
[i
] = Lerp(mag
[i
], r
[i
], f
);
1752 // Process the list of sources in the data set definition.
1753 auto ProcessSources(TokenReaderT
*tr
, HrirDataT
*hData
, const uint outRate
) -> int
1755 const uint channels
{(hData
->mChannelType
== CT_STEREO
) ? 2u : 1u};
1756 hData
->mHrirsBase
.resize(size_t{channels
} * hData
->mIrCount
* hData
->mIrSize
);
1757 const auto hrirs
= al::span
<double>{hData
->mHrirsBase
};
1758 auto hrir
= std::vector
<double>(hData
->mIrSize
);
1759 uint line
, col
, fi
, ei
, ai
;
1761 std::vector
<double> onsetSamples(size_t{OnsetRateMultiple
} * hData
->mIrPoints
);
1762 PPhaseResampler onsetResampler
;
1763 onsetResampler
.init(hData
->mIrRate
, OnsetRateMultiple
*hData
->mIrRate
);
1765 std::optional
<PPhaseResampler
> resampler
;
1766 if(outRate
&& outRate
!= hData
->mIrRate
)
1767 resampler
.emplace().init(hData
->mIrRate
, outRate
);
1768 const double rateScale
{outRate
? static_cast<double>(outRate
) / hData
->mIrRate
: 1.0};
1769 const uint irPoints
{outRate
1770 ? std::min(static_cast<uint
>(std::ceil(hData
->mIrPoints
*rateScale
)), hData
->mIrPoints
)
1771 : hData
->mIrPoints
};
1773 printf("Loading sources...");
1776 while(TrIsOperator(tr
, "["))
1778 std::array factor
{1.0, 1.0};
1780 TrIndication(tr
, &line
, &col
);
1781 TrReadOperator(tr
, "[");
1783 if(TrIsOperator(tr
, "*"))
1785 TrReadOperator(tr
, "*");
1786 if(!TrReadOperator(tr
, "]") || !TrReadOperator(tr
, "="))
1789 TrIndication(tr
, &line
, &col
);
1791 if(!ReadSofaRef(tr
, &src
))
1794 if(hData
->mChannelType
== CT_STEREO
)
1796 std::array
<char,MaxIdentLen
+1> type
{};
1798 if(!TrReadIdent(tr
, type
))
1801 const ChannelTypeT channelType
{MatchChannelType(type
.data())};
1805 TrErrorAt(tr
, line
, col
, "Expected a channel type.\n");
1817 std::array
<char,MaxIdentLen
+1> type
{};
1818 if(!TrReadIdent(tr
, type
))
1821 ChannelTypeT channelType
{MatchChannelType(type
.data())};
1822 if(channelType
!= CT_MONO
)
1824 TrErrorAt(tr
, line
, col
, "Expected a mono channel type.\n");
1830 MYSOFA_EASY
*sofa
{LoadSofaFile(&src
, hData
->mIrRate
, hData
->mIrPoints
)};
1833 const auto srcPosValues
= al::span
{sofa
->hrtf
->SourcePosition
.values
,
1834 sofa
->hrtf
->M
*3_uz
};
1835 for(uint si
{0};si
< sofa
->hrtf
->M
;++si
)
1837 printf("\rLoading sources... %d of %d", si
+1, sofa
->hrtf
->M
);
1840 std::array aer
{srcPosValues
[3_uz
*si
], srcPosValues
[3_uz
*si
+ 1],
1841 srcPosValues
[3_uz
*si
+ 2]};
1842 mysofa_c2s(aer
.data());
1844 if(std::fabs(aer
[1]) >= 89.999f
)
1847 aer
[0] = std::fmod(360.0f
- aer
[0], 360.0f
);
1849 auto field
= std::find_if(hData
->mFds
.cbegin(), hData
->mFds
.cend(),
1850 [&aer
](const HrirFdT
&fld
) -> bool
1851 { return (std::abs(aer
[2] - fld
.mDistance
) < 0.001); });
1852 if(field
== hData
->mFds
.cend())
1854 fi
= static_cast<uint
>(std::distance(hData
->mFds
.cbegin(), field
));
1856 const double evscale
{180.0 / static_cast<double>(field
->mEvs
.size()-1)};
1857 double ef
{(90.0 + aer
[1]) / evscale
};
1858 ei
= static_cast<uint
>(std::round(ef
));
1859 ef
= (ef
- ei
) * evscale
;
1860 if(std::abs(ef
) >= 0.1)
1863 const double azscale
{360.0 / static_cast<double>(field
->mEvs
[ei
].mAzs
.size())};
1864 double af
{aer
[0] / azscale
};
1865 ai
= static_cast<uint
>(std::round(af
));
1866 af
= (af
- ai
) * azscale
;
1867 ai
%= static_cast<uint
>(field
->mEvs
[ei
].mAzs
.size());
1868 if(std::abs(af
) >= 0.1)
1871 HrirAzT
*azd
= &field
->mEvs
[ei
].mAzs
[ai
];
1872 if(!azd
->mIrs
[0].empty())
1874 TrErrorAt(tr
, line
, col
, "Redefinition of source [ %d, %d, %d ].\n", fi
, ei
, ai
);
1878 const auto hrirPoints
= al::span
{hrir
}.first(hData
->mIrPoints
);
1879 ExtractSofaHrir(sofa
->hrtf
, si
, 0, src
.mOffset
, hrirPoints
);
1880 azd
->mIrs
[0] = hrirs
.subspan(size_t{hData
->mIrSize
}*azd
->mIndex
, hData
->mIrSize
);
1881 azd
->mDelays
[0] = AverageHrirOnset(onsetResampler
, onsetSamples
, hData
->mIrRate
,
1882 hrirPoints
, 1.0, azd
->mDelays
[0]);
1884 resampler
->process(hrirPoints
, hrir
);
1885 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0,
1888 if(src
.mChannel
== 1)
1890 ExtractSofaHrir(sofa
->hrtf
, si
, 1, src
.mOffset
, hrirPoints
);
1891 azd
->mIrs
[1] = hrirs
.subspan(
1892 (size_t{hData
->mIrCount
}+azd
->mIndex
) * hData
->mIrSize
, hData
->mIrSize
);
1893 azd
->mDelays
[1] = AverageHrirOnset(onsetResampler
, onsetSamples
,
1894 hData
->mIrRate
, hrirPoints
, 1.0, azd
->mDelays
[1]);
1896 resampler
->process(hrirPoints
, hrir
);
1897 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0,
1901 // TODO: Since some SOFA files contain minimum phase HRIRs,
1902 // it would be beneficial to check for per-measurement delays
1903 // (when available) to reconstruct the HRTDs.
1909 if(!ReadIndexTriplet(tr
, hData
, &fi
, &ei
, &ai
))
1911 if(!TrReadOperator(tr
, "]"))
1913 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
1915 if(!azd
->mIrs
[0].empty())
1917 TrErrorAt(tr
, line
, col
, "Redefinition of source.\n");
1920 if(!TrReadOperator(tr
, "="))
1926 if(!ReadSourceRef(tr
, &src
))
1929 // TODO: Would be nice to display 'x of y files', but that would
1930 // require preparing the source refs first to get a total count
1931 // before loading them.
1933 printf("\rLoading sources... %d file%s", count
, (count
==1)?"":"s");
1936 if(!LoadSource(&src
, hData
->mIrRate
, al::span
{hrir
}.first(hData
->mIrPoints
)))
1940 if(hData
->mChannelType
== CT_STEREO
)
1942 std::array
<char,MaxIdentLen
+1> ident
{};
1943 if(!TrReadIdent(tr
, ident
))
1945 ti
= static_cast<uint
>(MatchTargetEar(ident
.data()));
1946 if(static_cast<int>(ti
) < 0)
1948 TrErrorAt(tr
, line
, col
, "Expected a target ear.\n");
1952 const auto hrirPoints
= al::span
{hrir
}.first(hData
->mIrPoints
);
1953 azd
->mIrs
[ti
] = hrirs
.subspan((ti
*size_t{hData
->mIrCount
}+azd
->mIndex
)*hData
->mIrSize
,
1955 azd
->mDelays
[ti
] = AverageHrirOnset(onsetResampler
, onsetSamples
, hData
->mIrRate
,
1956 hrirPoints
, 1.0/factor
[ti
], azd
->mDelays
[ti
]);
1958 resampler
->process(hrirPoints
, hrir
);
1959 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0/factor
[ti
],
1962 if(!TrIsOperator(tr
, "+"))
1964 TrReadOperator(tr
, "+");
1966 if(hData
->mChannelType
== CT_STEREO
)
1968 if(azd
->mIrs
[0].empty())
1970 TrErrorAt(tr
, line
, col
, "Missing left ear source reference(s).\n");
1973 if(azd
->mIrs
[1].empty())
1975 TrErrorAt(tr
, line
, col
, "Missing right ear source reference(s).\n");
1984 hData
->mIrRate
= outRate
;
1985 hData
->mIrPoints
= irPoints
;
1988 for(fi
= 0;fi
< hData
->mFds
.size();fi
++)
1990 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
1992 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
1994 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
1995 if(!azd
->mIrs
[0].empty())
1998 if(ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size())
2001 if(ei
>= hData
->mFds
[fi
].mEvs
.size())
2003 TrError(tr
, "Missing source references [ %d, *, * ].\n", fi
);
2006 hData
->mFds
[fi
].mEvStart
= ei
;
2007 for(;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
2009 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
2011 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
2013 if(azd
->mIrs
[0].empty())
2015 TrError(tr
, "Missing source reference [ %d, %d, %d ].\n", fi
, ei
, ai
);
2021 for(uint ti
{0};ti
< channels
;ti
++)
2023 for(fi
= 0;fi
< hData
->mFds
.size();fi
++)
2025 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
2027 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
2029 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
2030 azd
->mIrs
[ti
] = hrirs
.subspan(
2031 (ti
*size_t{hData
->mIrCount
} + azd
->mIndex
) * hData
->mIrSize
,
2043 TrError(tr
, "Errant data at end of source list.\n");
2050 bool LoadDefInput(std::istream
&istream
, const al::span
<const char> startbytes
,
2051 const std::string_view filename
, const uint fftSize
, const uint truncSize
, const uint outRate
,
2052 const ChannelModeT chanMode
, HrirDataT
*hData
)
2054 TokenReaderT tr
{istream
};
2056 TrSetup(startbytes
, filename
, &tr
);
2057 if(!ProcessMetrics(&tr
, fftSize
, truncSize
, chanMode
, hData
)
2058 || !ProcessSources(&tr
, hData
, outRate
))