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"
55 // Constants for accessing the token reader's ring buffer.
56 constexpr uint TRRingBits
{16};
57 constexpr uint TRRingSize
{1 << TRRingBits
};
58 constexpr uint TRRingMask
{TRRingSize
- 1};
60 // The token reader's load interval in bytes.
61 constexpr uint TRLoadSize
{TRRingSize
>> 2};
63 // Token reader state for parsing the data set definition.
65 std::istream
&mIStream
;
69 std::array
<char,TRRingSize
> mRing
{};
70 std::streamsize mIn
{};
71 std::streamsize mOut
{};
73 TokenReaderT(std::istream
&istream
) noexcept
: mIStream
{istream
} { }
74 TokenReaderT(const TokenReaderT
&) = default;
78 // The maximum identifier length used when processing the data set
80 constexpr uint MaxIdentLen
{16};
82 // The limits for the listener's head 'radius' in the data set definition.
83 constexpr double MinRadius
{0.05};
84 constexpr double MaxRadius
{0.15};
86 // The maximum number of channels that can be addressed for a WAVE file
87 // source listed in the data set definition.
88 constexpr uint MaxWaveChannels
{65535};
90 // The limits to the byte size for a binary source listed in the definition
97 // The limits to the number of significant bits for an ASCII source listed in
98 // the data set definition.
104 // The four-character-codes for RIFF/RIFX WAVE file chunks.
106 FOURCC_RIFF
= 0x46464952, // 'RIFF'
107 FOURCC_RIFX
= 0x58464952, // 'RIFX'
108 FOURCC_WAVE
= 0x45564157, // 'WAVE'
109 FOURCC_FMT
= 0x20746D66, // 'fmt '
110 FOURCC_DATA
= 0x61746164, // 'data'
111 FOURCC_LIST
= 0x5453494C, // 'LIST'
112 FOURCC_WAVL
= 0x6C766177, // 'wavl'
113 FOURCC_SLNT
= 0x746E6C73, // 'slnt'
116 // The supported wave formats.
118 WAVE_FORMAT_PCM
= 0x0001,
119 WAVE_FORMAT_IEEE_FLOAT
= 0x0003,
120 WAVE_FORMAT_EXTENSIBLE
= 0xFFFE,
130 // Source format for the references listed in the data set definition.
133 SF_ASCII
, // ASCII text file.
134 SF_BIN_LE
, // Little-endian binary file.
135 SF_BIN_BE
, // Big-endian binary file.
136 SF_WAVE
, // RIFF/RIFX WAVE file.
137 SF_SOFA
// Spatially Oriented Format for Accoustics (SOFA) file.
140 // Element types for the references listed in the data set definition.
143 ET_INT
, // Integer elements.
144 ET_FP
// Floating-point elements.
147 // Source reference state used when loading sources.
149 SourceFormatT mFormat
;
159 std::array
<char,MAX_PATH_LEN
+1> mPath
;
163 /* Whitespace is not significant. It can process tokens as identifiers, numbers
164 * (integer and floating-point), strings, and operators. Strings must be
165 * encapsulated by double-quotes and cannot span multiple lines.
168 // Setup the reader on the given file. The filename can be NULL if no error
169 // output is desired.
170 void TrSetup(const al::span
<const char> startbytes
, const std::string_view filename
,
173 std::string_view namepart
;
175 if(!filename
.empty())
177 const auto fslashpos
= filename
.rfind('/');
178 const auto bslashpos
= filename
.rfind('\\');
179 const auto slashpos
= (bslashpos
>= filename
.size()) ? fslashpos
:
180 (fslashpos
>= filename
.size()) ? bslashpos
:
181 std::max(fslashpos
, bslashpos
);
182 if(slashpos
< filename
.size())
183 namepart
= filename
.substr(slashpos
+1);
186 tr
->mName
= namepart
;
192 if(!startbytes
.empty())
194 assert(startbytes
.size() <= tr
->mRing
.size());
195 std::copy(startbytes
.cbegin(), startbytes
.cend(), tr
->mRing
.begin());
196 tr
->mIn
+= std::streamsize(startbytes
.size());
200 // Prime the reader's ring buffer, and return a result indicating that there
201 // is text to process.
202 auto TrLoad(TokenReaderT
*tr
) -> int
204 std::istream
&istream
= tr
->mIStream
;
206 std::streamsize toLoad
{TRRingSize
- static_cast<std::streamsize
>(tr
->mIn
- tr
->mOut
)};
207 if(toLoad
>= TRLoadSize
&& istream
.good())
209 // Load TRLoadSize (or less if at the end of the file) per read.
212 const auto in
= tr
->mIn
&TRRingMask
;
213 std::streamsize count
{TRRingSize
- in
};
216 istream
.read(al::to_address(tr
->mRing
.begin() + in
), count
);
217 tr
->mIn
+= istream
.gcount();
218 istream
.read(tr
->mRing
.data(), toLoad
-count
);
219 tr
->mIn
+= istream
.gcount();
223 istream
.read(al::to_address(tr
->mRing
.begin() + in
), toLoad
);
224 tr
->mIn
+= istream
.gcount();
227 if(tr
->mOut
>= TRRingSize
)
229 tr
->mOut
-= TRRingSize
;
230 tr
->mIn
-= TRRingSize
;
233 if(tr
->mIn
> tr
->mOut
)
238 // Error display routine. Only displays when the base name is not NULL.
239 void TrErrorVA(const TokenReaderT
*tr
, uint line
, uint column
, const char *format
, va_list argPtr
)
241 if(tr
->mName
.empty())
243 fprintf(stderr
, "\nError (%s:%u:%u): ", tr
->mName
.c_str(), line
, column
);
244 vfprintf(stderr
, format
, argPtr
);
247 // Used to display an error at a saved line/column.
248 void TrErrorAt(const TokenReaderT
*tr
, uint line
, uint column
, const char *format
, ...)
250 /* NOLINTBEGIN(*-array-to-pointer-decay) */
252 va_start(argPtr
, format
);
253 TrErrorVA(tr
, line
, column
, format
, argPtr
);
255 /* NOLINTEND(*-array-to-pointer-decay) */
258 // Used to display an error at the current line/column.
259 void TrError(const TokenReaderT
*tr
, const char *format
, ...)
261 /* NOLINTBEGIN(*-array-to-pointer-decay) */
263 va_start(argPtr
, format
);
264 TrErrorVA(tr
, tr
->mLine
, tr
->mColumn
, format
, argPtr
);
266 /* NOLINTEND(*-array-to-pointer-decay) */
269 // Skips to the next line.
270 void TrSkipLine(TokenReaderT
*tr
)
276 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
288 // Skips to the next token.
289 auto TrSkipWhitespace(TokenReaderT
*tr
) -> int
293 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
313 // Get the line and/or column of the next token (or the end of input).
314 void TrIndication(TokenReaderT
*tr
, uint
*line
, uint
*column
)
316 TrSkipWhitespace(tr
);
317 if(line
) *line
= tr
->mLine
;
318 if(column
) *column
= tr
->mColumn
;
321 // Checks to see if a token is (likely to be) an identifier. It does not
322 // display any errors and will not proceed to the next token.
323 auto TrIsIdent(TokenReaderT
*tr
) -> int
325 if(!TrSkipWhitespace(tr
))
327 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
328 return ch
== '_' || isalpha(ch
);
332 // Checks to see if a token is the given operator. It does not display any
333 // errors and will not proceed to the next token.
334 auto TrIsOperator(TokenReaderT
*tr
, const std::string_view op
) -> int
336 if(!TrSkipWhitespace(tr
))
340 while(len
< op
.size() && out
< tr
->mIn
)
342 if(tr
->mRing
[out
&TRRingMask
] != op
[len
])
352 /* The TrRead*() routines obtain the value of a matching token type. They
353 * display type, form, and boundary errors and will proceed to the next
357 // Reads and validates an identifier token.
358 auto TrReadIdent(TokenReaderT
*tr
, const al::span
<char> ident
) -> int
360 assert(!ident
.empty());
361 const size_t maxLen
{ident
.size()-1};
362 uint col
{tr
->mColumn
};
363 if(TrSkipWhitespace(tr
))
366 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
367 if(ch
== '_' || isalpha(ch
))
377 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
378 } while(ch
== '_' || isdigit(ch
) || isalpha(ch
));
380 tr
->mColumn
+= static_cast<uint
>(len
);
386 TrErrorAt(tr
, tr
->mLine
, col
, "Identifier is too long.\n");
390 TrErrorAt(tr
, tr
->mLine
, col
, "Expected an identifier.\n");
394 // Reads and validates (including bounds) an integer token.
395 auto TrReadInt(TokenReaderT
*tr
, const int loBound
, const int hiBound
, int *value
) -> int
397 uint col
{tr
->mColumn
};
398 if(TrSkipWhitespace(tr
))
402 std::array
<char,64+1> temp
{};
403 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
404 if(ch
== '+' || ch
== '-')
413 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
414 if(!isdigit(ch
)) break;
422 if(digis
> 0 && ch
!= '.' && !isalpha(ch
))
426 TrErrorAt(tr
, tr
->mLine
, col
, "Integer is too long.");
430 *value
= static_cast<int>(strtol(temp
.data(), nullptr, 10));
431 if(*value
< loBound
|| *value
> hiBound
)
433 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a value from %d to %d.\n", loBound
, hiBound
);
439 TrErrorAt(tr
, tr
->mLine
, col
, "Expected an integer.\n");
443 // Reads and validates (including bounds) a float token.
444 auto TrReadFloat(TokenReaderT
*tr
, const double loBound
, const double hiBound
, double *value
) -> int
446 uint col
{tr
->mColumn
};
447 if(TrSkipWhitespace(tr
))
450 std::array
<char,64+1> temp
{};
452 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
453 if(ch
== '+' || ch
== '-')
463 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
464 if(!isdigit(ch
)) break;
480 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
481 if(!isdigit(ch
)) break;
490 if(ch
== 'E' || ch
== 'e')
497 if(ch
== '+' || ch
== '-')
506 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
507 if(!isdigit(ch
)) break;
516 if(digis
> 0 && ch
!= '.' && !isalpha(ch
))
520 TrErrorAt(tr
, tr
->mLine
, col
, "Float is too long.");
524 *value
= strtod(temp
.data(), nullptr);
525 if(*value
< loBound
|| *value
> hiBound
)
527 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a value from %f to %f.\n", loBound
, hiBound
);
536 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a float.\n");
540 // Reads and validates a string token.
541 auto TrReadString(TokenReaderT
*tr
, const al::span
<char> text
) -> int
543 assert(!text
.empty());
544 const size_t maxLen
{text
.size()-1};
546 uint col
{tr
->mColumn
};
547 if(TrSkipWhitespace(tr
))
550 if(char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]}; ch
== '\"')
556 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
562 TrErrorAt(tr
, tr
->mLine
, col
, "Unterminated string at end of line.\n");
571 tr
->mColumn
+= static_cast<uint
>(1 + len
);
572 TrErrorAt(tr
, tr
->mLine
, col
, "Unterminated string at end of input.\n");
575 tr
->mColumn
+= static_cast<uint
>(2 + len
);
578 TrErrorAt(tr
, tr
->mLine
, col
, "String is too long.\n");
585 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a string.\n");
589 // Reads and validates the given operator.
590 auto TrReadOperator(TokenReaderT
*tr
, const std::string_view op
) -> int
592 uint col
{tr
->mColumn
};
593 if(TrSkipWhitespace(tr
))
597 while(len
< op
.size() && TrLoad(tr
))
599 if(tr
->mRing
[tr
->mOut
&TRRingMask
] != op
[len
])
604 tr
->mColumn
+= static_cast<uint
>(len
);
608 TrErrorAt(tr
, tr
->mLine
, col
, "Expected '%s' operator.\n", op
);
613 /*************************
614 *** File source input ***
615 *************************/
617 // Read a binary value of the specified byte order and byte size from a file,
618 // storing it as a 32-bit unsigned integer.
619 auto ReadBin4(std::istream
&istream
, const char *filename
, const ByteOrderT order
,
620 const uint bytes
, uint32_t *out
) -> int
622 std::array
<uint8_t,4> in
{};
623 istream
.read(reinterpret_cast<char*>(in
.data()), static_cast<int>(bytes
));
624 if(istream
.gcount() != bytes
)
626 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
633 for(uint i
= 0;i
< bytes
;i
++)
634 accum
= (accum
<<8) | in
[bytes
- i
- 1];
637 for(uint i
= 0;i
< bytes
;i
++)
638 accum
= (accum
<<8) | in
[i
];
647 // Read a binary value of the specified byte order from a file, storing it as
648 // a 64-bit unsigned integer.
649 auto ReadBin8(std::istream
&istream
, const char *filename
, const ByteOrderT order
, uint64_t *out
) -> int
651 std::array
<uint8_t,8> in
{};
652 istream
.read(reinterpret_cast<char*>(in
.data()), 8);
653 if(istream
.gcount() != 8)
655 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
663 for(uint i
{0};i
< 8;++i
)
664 accum
= (accum
<<8) | in
[8 - i
- 1];
667 for(uint i
{0};i
< 8;++i
)
668 accum
= (accum
<<8) | in
[i
];
677 /* Read a binary value of the specified type, byte order, and byte size from
678 * a file, converting it to a double. For integer types, the significant
679 * bits are used to normalize the result. The sign of bits determines
680 * whether they are padded toward the MSB (negative) or LSB (positive).
681 * Floating-point types are not normalized.
683 auto ReadBinAsDouble(std::istream
&istream
, const char *filename
, const ByteOrderT order
,
684 const ElementTypeT type
, const uint bytes
, const int bits
, double *out
) -> int
690 if(!ReadBin8(istream
, filename
, order
, &val
))
693 *out
= al::bit_cast
<double>(val
);
698 if(!ReadBin4(istream
, filename
, order
, bytes
, &val
))
701 *out
= al::bit_cast
<float>(val
);
705 val
>>= (8*bytes
) - (static_cast<uint
>(bits
));
707 val
&= (0xFFFFFFFF >> (32+bits
));
709 if(val
&static_cast<uint
>(1<<(std::abs(bits
)-1)))
710 val
|= (0xFFFFFFFF << std::abs(bits
));
711 *out
= static_cast<int32_t>(val
) / static_cast<double>(1<<(std::abs(bits
)-1));
717 /* Read an ascii value of the specified type from a file, converting it to a
718 * double. For integer types, the significant bits are used to normalize the
719 * result. The sign of the bits should always be positive. This also skips
720 * up to one separator character before the element itself.
722 auto ReadAsciiAsDouble(TokenReaderT
*tr
, const char *filename
, const ElementTypeT type
,
723 const uint bits
, double *out
) -> int
725 if(TrIsOperator(tr
, ","))
726 TrReadOperator(tr
, ",");
727 else if(TrIsOperator(tr
, ":"))
728 TrReadOperator(tr
, ":");
729 else if(TrIsOperator(tr
, ";"))
730 TrReadOperator(tr
, ";");
731 else if(TrIsOperator(tr
, "|"))
732 TrReadOperator(tr
, "|");
736 if(!TrReadFloat(tr
, -std::numeric_limits
<double>::infinity(),
737 std::numeric_limits
<double>::infinity(), out
))
739 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
746 if(!TrReadInt(tr
, -(1<<(bits
-1)), (1<<(bits
-1))-1, &v
))
748 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
751 *out
= v
/ static_cast<double>((1<<(bits
-1))-1);
756 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
757 // the source parameters and data set metrics.
758 auto ReadWaveFormat(std::istream
&istream
, const ByteOrderT order
, const uint hrirRate
,
759 SourceRefT
*src
) -> int
761 uint32_t fourCC
, chunkSize
;
762 uint32_t format
, channels
, rate
, dummy
, block
, size
, bits
;
767 istream
.seekg(static_cast<int>(chunkSize
), std::ios::cur
);
768 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
769 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
771 } while(fourCC
!= FOURCC_FMT
);
772 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &format
)
773 || !ReadBin4(istream
, src
->mPath
.data(), order
, 2, &channels
)
774 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &rate
)
775 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &dummy
)
776 || !ReadBin4(istream
, src
->mPath
.data(), order
, 2, &block
))
781 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &size
))
789 if(format
== WAVE_FORMAT_EXTENSIBLE
)
791 istream
.seekg(2, std::ios::cur
);
792 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &bits
))
796 istream
.seekg(4, std::ios::cur
);
797 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &format
))
799 istream
.seekg(static_cast<int>(chunkSize
- 26), std::ios::cur
);
805 istream
.seekg(static_cast<int>(chunkSize
- 16), std::ios::cur
);
807 istream
.seekg(static_cast<int>(chunkSize
- 14), std::ios::cur
);
809 if(format
!= WAVE_FORMAT_PCM
&& format
!= WAVE_FORMAT_IEEE_FLOAT
)
811 fprintf(stderr
, "\nError: Unsupported WAVE format in file '%s'.\n", src
->mPath
.data());
814 if(src
->mChannel
>= channels
)
816 fprintf(stderr
, "\nError: Missing source channel in WAVE file '%s'.\n", src
->mPath
.data());
821 fprintf(stderr
, "\nError: Mismatched source sample rate in WAVE file '%s'.\n",
825 if(format
== WAVE_FORMAT_PCM
)
827 if(size
< 2 || size
> 4)
829 fprintf(stderr
, "\nError: Unsupported sample size in WAVE file '%s'.\n",
833 if(bits
< 16 || bits
> (8*size
))
835 fprintf(stderr
, "\nError: Bad significant bits in WAVE file '%s'.\n",
843 if(size
!= 4 && size
!= 8)
845 fprintf(stderr
, "\nError: Unsupported sample size in WAVE file '%s'.\n",
852 src
->mBits
= static_cast<int>(bits
);
853 src
->mSkip
= channels
;
857 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
858 auto ReadWaveData(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
859 const al::span
<double> hrir
) -> int
861 auto pre
= static_cast<int>(src
->mSize
* src
->mChannel
);
862 auto post
= static_cast<int>(src
->mSize
* (src
->mSkip
- src
->mChannel
- 1));
864 for(size_t i
{0};i
< hrir
.size();++i
)
868 istream
.seekg(skip
, std::ios::cur
);
869 if(!ReadBinAsDouble(istream
, src
->mPath
.data(), order
, src
->mType
, src
->mSize
, src
->mBits
,
875 istream
.seekg(skip
, std::ios::cur
);
879 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
881 auto ReadWaveList(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
882 const al::span
<double> hrir
) -> int
884 uint32_t fourCC
, chunkSize
, listSize
, count
;
885 uint block
, skip
, offset
, i
;
890 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
891 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
894 if(fourCC
== FOURCC_DATA
)
896 block
= src
->mSize
* src
->mSkip
;
897 count
= chunkSize
/ block
;
898 if(count
< (src
->mOffset
+ hrir
.size()))
900 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", src
->mPath
.data());
903 using off_type
= std::istream::off_type
;
904 istream
.seekg(off_type(src
->mOffset
) * off_type(block
), std::ios::cur
);
905 if(!ReadWaveData(istream
, src
, order
, hrir
))
909 if(fourCC
== FOURCC_LIST
)
911 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
))
914 if(fourCC
== FOURCC_WAVL
)
918 istream
.seekg(static_cast<long>(chunkSize
), std::ios::cur
);
920 listSize
= chunkSize
;
921 block
= src
->mSize
* src
->mSkip
;
925 while(offset
< hrir
.size() && listSize
> 8)
927 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
928 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
930 listSize
-= 8 + chunkSize
;
931 if(fourCC
== FOURCC_DATA
)
933 count
= chunkSize
/ block
;
936 using off_type
= std::istream::off_type
;
937 istream
.seekg(off_type(skip
) * off_type(block
), std::ios::cur
);
938 chunkSize
-= skip
* block
;
941 if(count
> (hrir
.size() - offset
))
942 count
= static_cast<uint
>(hrir
.size() - offset
);
943 if(!ReadWaveData(istream
, src
, order
, hrir
.subspan(offset
, count
)))
945 chunkSize
-= count
* block
;
947 lastSample
= hrir
[offset
- 1];
955 else if(fourCC
== FOURCC_SLNT
)
957 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 4, &count
))
964 if(count
> (hrir
.size() - offset
))
965 count
= static_cast<uint
>(hrir
.size() - offset
);
966 for(i
= 0; i
< count
; i
++)
967 hrir
[offset
+ i
] = lastSample
;
977 istream
.seekg(static_cast<long>(chunkSize
), std::ios::cur
);
979 if(offset
< hrir
.size())
981 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", src
->mPath
.data());
987 // Load a source HRIR from an ASCII text file containing a list of elements
988 // separated by whitespace or common list operators (',', ';', ':', '|').
989 auto LoadAsciiSource(std::istream
&istream
, const SourceRefT
*src
, const al::span
<double> hrir
) -> int
991 TokenReaderT tr
{istream
};
993 TrSetup({}, {}, &tr
);
994 for(uint i
{0};i
< src
->mOffset
;++i
)
997 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
, static_cast<uint
>(src
->mBits
),
1001 for(size_t i
{0};i
< hrir
.size();++i
)
1003 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
, static_cast<uint
>(src
->mBits
),
1006 for(uint j
{0};j
< src
->mSkip
;++j
)
1009 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
,
1010 static_cast<uint
>(src
->mBits
), &dummy
))
1017 // Load a source HRIR from a binary file.
1018 auto LoadBinarySource(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
1019 const al::span
<double> hrir
) -> int
1021 istream
.seekg(static_cast<long>(src
->mOffset
), std::ios::beg
);
1022 for(size_t i
{0};i
< hrir
.size();++i
)
1024 if(!ReadBinAsDouble(istream
, src
->mPath
.data(), order
, src
->mType
, src
->mSize
, src
->mBits
,
1028 istream
.seekg(static_cast<long>(src
->mSkip
), std::ios::cur
);
1033 // Load a source HRIR from a RIFF/RIFX WAVE file.
1034 auto LoadWaveSource(std::istream
&istream
, SourceRefT
*src
, const uint hrirRate
,
1035 const al::span
<double> hrir
) -> int
1037 uint32_t fourCC
, dummy
;
1040 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
1041 || !ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &dummy
))
1043 if(fourCC
== FOURCC_RIFF
)
1045 else if(fourCC
== FOURCC_RIFX
)
1049 fprintf(stderr
, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src
->mPath
.data());
1053 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
))
1055 if(fourCC
!= FOURCC_WAVE
)
1057 fprintf(stderr
, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src
->mPath
.data());
1060 if(!ReadWaveFormat(istream
, order
, hrirRate
, src
))
1062 if(!ReadWaveList(istream
, src
, order
, hrir
))
1068 struct SofaEasyDeleter
{
1069 void operator()(gsl::owner
<MYSOFA_EASY
*> sofa
)
1071 if(sofa
->neighborhood
) mysofa_neighborhood_free(sofa
->neighborhood
);
1072 if(sofa
->lookup
) mysofa_lookup_free(sofa
->lookup
);
1073 if(sofa
->hrtf
) mysofa_free(sofa
->hrtf
);
1077 using SofaEasyPtr
= std::unique_ptr
<MYSOFA_EASY
,SofaEasyDeleter
>;
1079 struct SofaCacheEntry
{
1084 std::vector
<SofaCacheEntry
> gSofaCache
;
1086 // Load a Spatially Oriented Format for Accoustics (SOFA) file.
1087 auto LoadSofaFile(SourceRefT
*src
, const uint hrirRate
, const uint n
) -> MYSOFA_EASY
*
1089 const std::string_view srcname
{src
->mPath
.data()};
1090 auto iter
= std::find_if(gSofaCache
.begin(), gSofaCache
.end(),
1091 [srcname
,hrirRate
](SofaCacheEntry
&entry
) -> bool
1092 { return entry
.mName
== srcname
&& entry
.mSampleRate
== hrirRate
; });
1093 if(iter
!= gSofaCache
.end()) return iter
->mSofa
.get();
1095 SofaEasyPtr sofa
{new(std::nothrow
) MYSOFA_EASY
{}};
1098 fprintf(stderr
, "\nError: Out of memory.\n");
1101 sofa
->lookup
= nullptr;
1102 sofa
->neighborhood
= nullptr;
1105 sofa
->hrtf
= mysofa_load(src
->mPath
.data(), &err
);
1108 fprintf(stderr
, "\nError: Could not load source file '%s' (error: %d).\n",
1109 src
->mPath
.data(), err
);
1112 /* NOTE: Some valid SOFA files are failing this check. */
1113 err
= mysofa_check(sofa
->hrtf
);
1114 if(err
!= MYSOFA_OK
)
1115 fprintf(stderr
, "\nWarning: Supposedly malformed source file '%s' (error: %d).\n",
1116 src
->mPath
.data(), err
);
1117 if((src
->mOffset
+ n
) > sofa
->hrtf
->N
)
1119 fprintf(stderr
, "\nError: Not enough samples in SOFA file '%s'.\n", src
->mPath
.data());
1122 if(src
->mChannel
>= sofa
->hrtf
->R
)
1124 fprintf(stderr
, "\nError: Missing source receiver in SOFA file '%s'.\n",src
->mPath
.data());
1127 mysofa_tocartesian(sofa
->hrtf
);
1128 sofa
->lookup
= mysofa_lookup_init(sofa
->hrtf
);
1129 if(sofa
->lookup
== nullptr)
1131 fprintf(stderr
, "\nError: Out of memory.\n");
1134 gSofaCache
.emplace_back(SofaCacheEntry
{std::string
{srcname
}, hrirRate
, std::move(sofa
)});
1135 return gSofaCache
.back().mSofa
.get();
1138 // Copies the HRIR data from a particular SOFA measurement.
1139 void ExtractSofaHrir(const MYSOFA_HRTF
*hrtf
, const size_t index
, const size_t channel
,
1140 const size_t offset
, const al::span
<double> hrir
)
1142 const auto irValues
= al::span
{hrtf
->DataIR
.values
, hrtf
->DataIR
.elements
}
1143 .subspan((index
*hrtf
->R
+ channel
)*hrtf
->N
+ offset
);
1144 std::copy_n(irValues
.cbegin(), hrir
.size(), hrir
.begin());
1147 // Load a source HRIR from a Spatially Oriented Format for Accoustics (SOFA)
1149 auto LoadSofaSource(SourceRefT
*src
, const uint hrirRate
, const al::span
<double> hrir
) -> int
1151 MYSOFA_EASY
*sofa
{LoadSofaFile(src
, hrirRate
, static_cast<uint
>(hrir
.size()))};
1152 if(sofa
== nullptr) return 0;
1154 /* NOTE: At some point it may be beneficial or necessary to consider the
1155 various coordinate systems, listener/source orientations, and
1156 directional vectors defined in the SOFA file.
1159 static_cast<float>(src
->mAzimuth
),
1160 static_cast<float>(src
->mElevation
),
1161 static_cast<float>(src
->mRadius
)
1163 mysofa_s2c(target
.data());
1165 int nearest
{mysofa_lookup(sofa
->lookup
, target
.data())};
1168 fprintf(stderr
, "\nError: Lookup failed in source file '%s'.\n", src
->mPath
.data());
1172 al::span
<float,3> coords
= al::span
{sofa
->hrtf
->SourcePosition
.values
, sofa
->hrtf
->M
*3_uz
}
1173 .subspan(static_cast<uint
>(nearest
)*3_uz
).first
<3>();
1174 if(std::abs(coords
[0] - target
[0]) > 0.001 || std::abs(coords
[1] - target
[1]) > 0.001
1175 || std::abs(coords
[2] - target
[2]) > 0.001)
1177 fprintf(stderr
, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n",
1178 src
->mRadius
, src
->mElevation
, src
->mAzimuth
, src
->mPath
.data());
1179 target
[0] = coords
[0];
1180 target
[1] = coords
[1];
1181 target
[2] = coords
[2];
1182 mysofa_c2s(target
.data());
1183 fprintf(stderr
, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target
[2],
1184 target
[1], target
[0]);
1188 ExtractSofaHrir(sofa
->hrtf
, static_cast<uint
>(nearest
), src
->mChannel
, src
->mOffset
, hrir
);
1193 // Load a source HRIR from a supported file type.
1194 auto LoadSource(SourceRefT
*src
, const uint hrirRate
, const al::span
<double> hrir
) -> int
1196 std::unique_ptr
<std::istream
> istream
;
1197 if(src
->mFormat
!= SF_SOFA
)
1199 if(src
->mFormat
== SF_ASCII
)
1200 istream
= std::make_unique
<std::ifstream
>(std::filesystem::u8path(src
->mPath
.data()));
1202 istream
= std::make_unique
<std::ifstream
>(std::filesystem::u8path(src
->mPath
.data()),
1204 if(!istream
->good())
1206 fprintf(stderr
, "\nError: Could not open source file '%s'.\n", src
->mPath
.data());
1211 switch(src
->mFormat
)
1213 case SF_ASCII
: return LoadAsciiSource(*istream
, src
, hrir
);
1214 case SF_BIN_LE
: return LoadBinarySource(*istream
, src
, BO_LITTLE
, hrir
);
1215 case SF_BIN_BE
: return LoadBinarySource(*istream
, src
, BO_BIG
, hrir
);
1216 case SF_WAVE
: return LoadWaveSource(*istream
, src
, hrirRate
, hrir
);
1217 case SF_SOFA
: return LoadSofaSource(src
, hrirRate
, hrir
);
1218 case SF_NONE
: break;
1224 // Match the channel type from a given identifier.
1225 auto MatchChannelType(const char *ident
) -> ChannelTypeT
1227 if(al::strcasecmp(ident
, "mono") == 0)
1229 if(al::strcasecmp(ident
, "stereo") == 0)
1235 // Process the data set definition to read and validate the data set metrics.
1236 auto ProcessMetrics(TokenReaderT
*tr
, const uint fftSize
, const uint truncSize
,
1237 const ChannelModeT chanMode
, HrirDataT
*hData
) -> int
1239 int hasRate
= 0, hasType
= 0, hasPoints
= 0, hasRadius
= 0;
1240 int hasDistance
= 0, hasAzimuths
= 0;
1241 std::array
<char,MaxIdentLen
+1> ident
;
1246 std::array
<double,MAX_FD_COUNT
> distances
;
1248 std::array
<uint
,MAX_FD_COUNT
> evCounts
;
1249 auto azCounts
= std::vector
<std::array
<uint
,MAX_EV_COUNT
>>(MAX_FD_COUNT
);
1250 for(auto &azs
: azCounts
) azs
.fill(0u);
1252 TrIndication(tr
, &line
, &col
);
1253 while(TrIsIdent(tr
))
1255 TrIndication(tr
, &line
, &col
);
1256 if(!TrReadIdent(tr
, ident
))
1258 if(al::strcasecmp(ident
.data(), "rate") == 0)
1262 TrErrorAt(tr
, line
, col
, "Redefinition of 'rate'.\n");
1265 if(!TrReadOperator(tr
, "="))
1267 if(!TrReadInt(tr
, MIN_RATE
, MAX_RATE
, &intVal
))
1269 hData
->mIrRate
= static_cast<uint
>(intVal
);
1272 else if(al::strcasecmp(ident
.data(), "type") == 0)
1274 std::array
<char,MaxIdentLen
+1> type
;
1278 TrErrorAt(tr
, line
, col
, "Redefinition of 'type'.\n");
1281 if(!TrReadOperator(tr
, "="))
1284 if(!TrReadIdent(tr
, type
))
1286 hData
->mChannelType
= MatchChannelType(type
.data());
1287 if(hData
->mChannelType
== CT_NONE
)
1289 TrErrorAt(tr
, line
, col
, "Expected a channel type.\n");
1292 if(hData
->mChannelType
== CT_STEREO
)
1294 if(chanMode
== CM_ForceMono
)
1295 hData
->mChannelType
= CT_MONO
;
1299 else if(al::strcasecmp(ident
.data(), "points") == 0)
1303 TrErrorAt(tr
, line
, col
, "Redefinition of 'points'.\n");
1306 if(!TrReadOperator(tr
, "="))
1308 TrIndication(tr
, &line
, &col
);
1309 if(!TrReadInt(tr
, MIN_POINTS
, MAX_POINTS
, &intVal
))
1311 points
= static_cast<uint
>(intVal
);
1312 if(fftSize
> 0 && points
> fftSize
)
1314 TrErrorAt(tr
, line
, col
, "Value exceeds the overridden FFT size.\n");
1317 if(points
< truncSize
)
1319 TrErrorAt(tr
, line
, col
, "Value is below the truncation size.\n");
1322 hData
->mIrPoints
= points
;
1323 hData
->mFftSize
= fftSize
;
1324 hData
->mIrSize
= 1 + (fftSize
/ 2);
1325 if(points
> hData
->mIrSize
)
1326 hData
->mIrSize
= points
;
1329 else if(al::strcasecmp(ident
.data(), "radius") == 0)
1333 TrErrorAt(tr
, line
, col
, "Redefinition of 'radius'.\n");
1336 if(!TrReadOperator(tr
, "="))
1338 if(!TrReadFloat(tr
, MinRadius
, MaxRadius
, &fpVal
))
1340 hData
->mRadius
= fpVal
;
1343 else if(al::strcasecmp(ident
.data(), "distance") == 0)
1349 TrErrorAt(tr
, line
, col
, "Redefinition of 'distance'.\n");
1352 if(!TrReadOperator(tr
, "="))
1357 if(!TrReadFloat(tr
, MIN_DISTANCE
, MAX_DISTANCE
, &fpVal
))
1359 if(count
> 0 && fpVal
<= distances
[count
- 1])
1361 TrError(tr
, "Distances are not ascending.\n");
1364 distances
[count
++] = fpVal
;
1365 if(!TrIsOperator(tr
, ","))
1367 if(count
>= MAX_FD_COUNT
)
1369 TrError(tr
, "Exceeded the maximum of %d fields.\n", MAX_FD_COUNT
);
1372 TrReadOperator(tr
, ",");
1374 if(fdCount
!= 0 && count
!= fdCount
)
1376 TrError(tr
, "Did not match the specified number of %d fields.\n", fdCount
);
1382 else if(al::strcasecmp(ident
.data(), "azimuths") == 0)
1388 TrErrorAt(tr
, line
, col
, "Redefinition of 'azimuths'.\n");
1391 if(!TrReadOperator(tr
, "="))
1397 if(!TrReadInt(tr
, MIN_AZ_COUNT
, MAX_AZ_COUNT
, &intVal
))
1399 azCounts
[count
][evCounts
[count
]++] = static_cast<uint
>(intVal
);
1400 if(TrIsOperator(tr
, ","))
1402 if(evCounts
[count
] >= MAX_EV_COUNT
)
1404 TrError(tr
, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT
);
1407 TrReadOperator(tr
, ",");
1411 if(evCounts
[count
] < MIN_EV_COUNT
)
1413 TrErrorAt(tr
, line
, col
, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT
);
1416 if(azCounts
[count
][0] != 1 || azCounts
[count
][evCounts
[count
] - 1] != 1)
1418 TrError(tr
, "Poles are not singular for field %d.\n", count
- 1);
1422 if(!TrIsOperator(tr
, ";"))
1425 if(count
>= MAX_FD_COUNT
)
1427 TrError(tr
, "Exceeded the maximum number of %d fields.\n", MAX_FD_COUNT
);
1430 evCounts
[count
] = 0;
1431 TrReadOperator(tr
, ";");
1434 if(fdCount
!= 0 && count
!= fdCount
)
1436 TrError(tr
, "Did not match the specified number of %d fields.\n", fdCount
);
1444 TrErrorAt(tr
, line
, col
, "Expected a metric name.\n");
1447 TrSkipWhitespace(tr
);
1449 if(!(hasRate
&& hasPoints
&& hasRadius
&& hasDistance
&& hasAzimuths
))
1451 TrErrorAt(tr
, line
, col
, "Expected a metric name.\n");
1454 if(distances
[0] < hData
->mRadius
)
1456 TrError(tr
, "Distance cannot start below head radius.\n");
1459 if(hData
->mChannelType
== CT_NONE
)
1460 hData
->mChannelType
= CT_MONO
;
1461 const auto azs
= al::span
{azCounts
}.first
<MAX_FD_COUNT
>();
1462 if(!PrepareHrirData(al::span
{distances
}.first(fdCount
), evCounts
, azs
, hData
))
1464 fprintf(stderr
, "Error: Out of memory.\n");
1470 // Parse an index triplet from the data set definition.
1471 auto ReadIndexTriplet(TokenReaderT
*tr
, const HrirDataT
*hData
, uint
*fi
, uint
*ei
, uint
*ai
)->int
1475 if(hData
->mFds
.size() > 1)
1477 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
.size()-1), &intVal
))
1479 *fi
= static_cast<uint
>(intVal
);
1480 if(!TrReadOperator(tr
, ","))
1487 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
[*fi
].mEvs
.size()-1), &intVal
))
1489 *ei
= static_cast<uint
>(intVal
);
1490 if(!TrReadOperator(tr
, ","))
1492 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
[*fi
].mEvs
[*ei
].mAzs
.size()-1), &intVal
))
1494 *ai
= static_cast<uint
>(intVal
);
1498 // Match the source format from a given identifier.
1499 auto MatchSourceFormat(const char *ident
) -> SourceFormatT
1501 if(al::strcasecmp(ident
, "ascii") == 0)
1503 if(al::strcasecmp(ident
, "bin_le") == 0)
1505 if(al::strcasecmp(ident
, "bin_be") == 0)
1507 if(al::strcasecmp(ident
, "wave") == 0)
1509 if(al::strcasecmp(ident
, "sofa") == 0)
1514 // Match the source element type from a given identifier.
1515 auto MatchElementType(const char *ident
) -> ElementTypeT
1517 if(al::strcasecmp(ident
, "int") == 0)
1519 if(al::strcasecmp(ident
, "fp") == 0)
1524 // Parse and validate a source reference from the data set definition.
1525 auto ReadSourceRef(TokenReaderT
*tr
, SourceRefT
*src
) -> int
1527 std::array
<char,MaxIdentLen
+1> ident
;
1532 TrIndication(tr
, &line
, &col
);
1533 if(!TrReadIdent(tr
, ident
))
1535 src
->mFormat
= MatchSourceFormat(ident
.data());
1536 if(src
->mFormat
== SF_NONE
)
1538 TrErrorAt(tr
, line
, col
, "Expected a source format.\n");
1541 if(!TrReadOperator(tr
, "("))
1543 if(src
->mFormat
== SF_SOFA
)
1545 if(!TrReadFloat(tr
, MIN_DISTANCE
, MAX_DISTANCE
, &fpVal
))
1547 src
->mRadius
= fpVal
;
1548 if(!TrReadOperator(tr
, ","))
1550 if(!TrReadFloat(tr
, -90.0, 90.0, &fpVal
))
1552 src
->mElevation
= fpVal
;
1553 if(!TrReadOperator(tr
, ","))
1555 if(!TrReadFloat(tr
, -360.0, 360.0, &fpVal
))
1557 src
->mAzimuth
= fpVal
;
1558 if(!TrReadOperator(tr
, ":"))
1560 if(!TrReadInt(tr
, 0, MaxWaveChannels
, &intVal
))
1562 src
->mType
= ET_NONE
;
1565 src
->mChannel
= static_cast<uint
>(intVal
);
1568 else if(src
->mFormat
== SF_WAVE
)
1570 if(!TrReadInt(tr
, 0, MaxWaveChannels
, &intVal
))
1572 src
->mType
= ET_NONE
;
1575 src
->mChannel
= static_cast<uint
>(intVal
);
1580 TrIndication(tr
, &line
, &col
);
1581 if(!TrReadIdent(tr
, ident
))
1583 src
->mType
= MatchElementType(ident
.data());
1584 if(src
->mType
== ET_NONE
)
1586 TrErrorAt(tr
, line
, col
, "Expected a source element type.\n");
1589 if(src
->mFormat
== SF_BIN_LE
|| src
->mFormat
== SF_BIN_BE
)
1591 if(!TrReadOperator(tr
, ","))
1593 if(src
->mType
== ET_INT
)
1595 if(!TrReadInt(tr
, MinBinSize
, MaxBinSize
, &intVal
))
1597 src
->mSize
= static_cast<uint
>(intVal
);
1598 if(!TrIsOperator(tr
, ","))
1599 src
->mBits
= static_cast<int>(8*src
->mSize
);
1602 TrReadOperator(tr
, ",");
1603 TrIndication(tr
, &line
, &col
);
1604 if(!TrReadInt(tr
, -2147483647-1, 2147483647, &intVal
))
1606 if(std::abs(intVal
) < int{MinBinSize
}*8 || static_cast<uint
>(std::abs(intVal
)) > (8*src
->mSize
))
1608 TrErrorAt(tr
, line
, col
, "Expected a value of (+/-) %d to %d.\n", MinBinSize
*8, 8*src
->mSize
);
1611 src
->mBits
= intVal
;
1616 TrIndication(tr
, &line
, &col
);
1617 if(!TrReadInt(tr
, -2147483647-1, 2147483647, &intVal
))
1619 if(intVal
!= 4 && intVal
!= 8)
1621 TrErrorAt(tr
, line
, col
, "Expected a value of 4 or 8.\n");
1624 src
->mSize
= static_cast<uint
>(intVal
);
1628 else if(src
->mFormat
== SF_ASCII
&& src
->mType
== ET_INT
)
1630 if(!TrReadOperator(tr
, ","))
1632 if(!TrReadInt(tr
, MinASCIIBits
, MaxASCIIBits
, &intVal
))
1635 src
->mBits
= intVal
;
1643 if(!TrIsOperator(tr
, ";"))
1647 TrReadOperator(tr
, ";");
1648 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1650 src
->mSkip
= static_cast<uint
>(intVal
);
1653 if(!TrReadOperator(tr
, ")"))
1655 if(TrIsOperator(tr
, "@"))
1657 TrReadOperator(tr
, "@");
1658 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1660 src
->mOffset
= static_cast<uint
>(intVal
);
1664 if(!TrReadOperator(tr
, ":"))
1666 if(!TrReadString(tr
, src
->mPath
))
1671 // Parse and validate a SOFA source reference from the data set definition.
1672 auto ReadSofaRef(TokenReaderT
*tr
, SourceRefT
*src
) -> int
1674 std::array
<char,MaxIdentLen
+1> ident
;
1678 TrIndication(tr
, &line
, &col
);
1679 if(!TrReadIdent(tr
, ident
))
1681 src
->mFormat
= MatchSourceFormat(ident
.data());
1682 if(src
->mFormat
!= SF_SOFA
)
1684 TrErrorAt(tr
, line
, col
, "Expected the SOFA source format.\n");
1688 src
->mType
= ET_NONE
;
1694 if(TrIsOperator(tr
, "@"))
1696 TrReadOperator(tr
, "@");
1697 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1699 src
->mOffset
= static_cast<uint
>(intVal
);
1703 if(!TrReadOperator(tr
, ":"))
1705 if(!TrReadString(tr
, src
->mPath
))
1710 // Match the target ear (index) from a given identifier.
1711 auto MatchTargetEar(const char *ident
) -> int
1713 if(al::strcasecmp(ident
, "left") == 0)
1715 if(al::strcasecmp(ident
, "right") == 0)
1720 // Calculate the onset time of an HRIR and average it with any existing
1721 // timing for its field, elevation, azimuth, and ear.
1722 constexpr int OnsetRateMultiple
{10};
1723 auto AverageHrirOnset(PPhaseResampler
&rs
, al::span
<double> upsampled
, const uint rate
,
1724 const al::span
<const double> hrir
, const double f
, const double onset
) -> double
1726 rs
.process(hrir
, upsampled
);
1728 auto abs_lt
= [](const double &lhs
, const double &rhs
) -> bool
1729 { return std::abs(lhs
) < std::abs(rhs
); };
1730 auto iter
= std::max_element(upsampled
.cbegin(), upsampled
.cend(), abs_lt
);
1731 return Lerp(onset
, static_cast<double>(std::distance(upsampled
.cbegin(), iter
))/(10*rate
), f
);
1734 // Calculate the magnitude response of an HRIR and average it with any
1735 // existing responses for its field, elevation, azimuth, and ear.
1736 void AverageHrirMagnitude(const uint fftSize
, const al::span
<const double> hrir
, const double f
,
1737 const al::span
<double> mag
)
1739 const uint m
{1 + (fftSize
/2)};
1740 std::vector
<complex_d
> h(fftSize
);
1741 std::vector
<double> r(m
);
1743 auto hiter
= std::copy(hrir
.cbegin(), hrir
.cend(), h
.begin());
1744 std::fill(hiter
, h
.end(), 0.0);
1746 MagnitudeResponse(h
, r
);
1747 for(uint i
{0};i
< m
;++i
)
1748 mag
[i
] = Lerp(mag
[i
], r
[i
], f
);
1751 // Process the list of sources in the data set definition.
1752 auto ProcessSources(TokenReaderT
*tr
, HrirDataT
*hData
, const uint outRate
) -> int
1754 const uint channels
{(hData
->mChannelType
== CT_STEREO
) ? 2u : 1u};
1755 hData
->mHrirsBase
.resize(size_t{channels
} * hData
->mIrCount
* hData
->mIrSize
);
1756 const auto hrirs
= al::span
<double>{hData
->mHrirsBase
};
1757 auto hrir
= std::vector
<double>(hData
->mIrSize
);
1758 uint line
, col
, fi
, ei
, ai
;
1760 std::vector
<double> onsetSamples(size_t{OnsetRateMultiple
} * hData
->mIrPoints
);
1761 PPhaseResampler onsetResampler
;
1762 onsetResampler
.init(hData
->mIrRate
, OnsetRateMultiple
*hData
->mIrRate
);
1764 std::optional
<PPhaseResampler
> resampler
;
1765 if(outRate
&& outRate
!= hData
->mIrRate
)
1766 resampler
.emplace().init(hData
->mIrRate
, outRate
);
1767 const double rateScale
{outRate
? static_cast<double>(outRate
) / hData
->mIrRate
: 1.0};
1768 const uint irPoints
{outRate
1769 ? std::min(static_cast<uint
>(std::ceil(hData
->mIrPoints
*rateScale
)), hData
->mIrPoints
)
1770 : hData
->mIrPoints
};
1772 printf("Loading sources...");
1775 while(TrIsOperator(tr
, "["))
1777 std::array factor
{1.0, 1.0};
1779 TrIndication(tr
, &line
, &col
);
1780 TrReadOperator(tr
, "[");
1782 if(TrIsOperator(tr
, "*"))
1784 TrReadOperator(tr
, "*");
1785 if(!TrReadOperator(tr
, "]") || !TrReadOperator(tr
, "="))
1788 TrIndication(tr
, &line
, &col
);
1790 if(!ReadSofaRef(tr
, &src
))
1793 if(hData
->mChannelType
== CT_STEREO
)
1795 std::array
<char,MaxIdentLen
+1> type
{};
1797 if(!TrReadIdent(tr
, type
))
1800 const ChannelTypeT channelType
{MatchChannelType(type
.data())};
1804 TrErrorAt(tr
, line
, col
, "Expected a channel type.\n");
1816 std::array
<char,MaxIdentLen
+1> type
{};
1817 if(!TrReadIdent(tr
, type
))
1820 ChannelTypeT channelType
{MatchChannelType(type
.data())};
1821 if(channelType
!= CT_MONO
)
1823 TrErrorAt(tr
, line
, col
, "Expected a mono channel type.\n");
1829 MYSOFA_EASY
*sofa
{LoadSofaFile(&src
, hData
->mIrRate
, hData
->mIrPoints
)};
1832 const auto srcPosValues
= al::span
{sofa
->hrtf
->SourcePosition
.values
,
1833 sofa
->hrtf
->M
*3_uz
};
1834 for(uint si
{0};si
< sofa
->hrtf
->M
;++si
)
1836 printf("\rLoading sources... %d of %d", si
+1, sofa
->hrtf
->M
);
1839 std::array aer
{srcPosValues
[3_uz
*si
], srcPosValues
[3_uz
*si
+ 1],
1840 srcPosValues
[3_uz
*si
+ 2]};
1841 mysofa_c2s(aer
.data());
1843 if(std::fabs(aer
[1]) >= 89.999f
)
1846 aer
[0] = std::fmod(360.0f
- aer
[0], 360.0f
);
1848 auto field
= std::find_if(hData
->mFds
.cbegin(), hData
->mFds
.cend(),
1849 [&aer
](const HrirFdT
&fld
) -> bool
1850 { return (std::abs(aer
[2] - fld
.mDistance
) < 0.001); });
1851 if(field
== hData
->mFds
.cend())
1853 fi
= static_cast<uint
>(std::distance(hData
->mFds
.cbegin(), field
));
1855 const double evscale
{180.0 / static_cast<double>(field
->mEvs
.size()-1)};
1856 double ef
{(90.0 + aer
[1]) / evscale
};
1857 ei
= static_cast<uint
>(std::round(ef
));
1858 ef
= (ef
- ei
) * evscale
;
1859 if(std::abs(ef
) >= 0.1)
1862 const double azscale
{360.0 / static_cast<double>(field
->mEvs
[ei
].mAzs
.size())};
1863 double af
{aer
[0] / azscale
};
1864 ai
= static_cast<uint
>(std::round(af
));
1865 af
= (af
- ai
) * azscale
;
1866 ai
%= static_cast<uint
>(field
->mEvs
[ei
].mAzs
.size());
1867 if(std::abs(af
) >= 0.1)
1870 HrirAzT
*azd
= &field
->mEvs
[ei
].mAzs
[ai
];
1871 if(!azd
->mIrs
[0].empty())
1873 TrErrorAt(tr
, line
, col
, "Redefinition of source [ %d, %d, %d ].\n", fi
, ei
, ai
);
1877 const auto hrirPoints
= al::span
{hrir
}.first(hData
->mIrPoints
);
1878 ExtractSofaHrir(sofa
->hrtf
, si
, 0, src
.mOffset
, hrirPoints
);
1879 azd
->mIrs
[0] = hrirs
.subspan(size_t{hData
->mIrSize
}*azd
->mIndex
, hData
->mIrSize
);
1880 azd
->mDelays
[0] = AverageHrirOnset(onsetResampler
, onsetSamples
, hData
->mIrRate
,
1881 hrirPoints
, 1.0, azd
->mDelays
[0]);
1883 resampler
->process(hrirPoints
, hrir
);
1884 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0,
1887 if(src
.mChannel
== 1)
1889 ExtractSofaHrir(sofa
->hrtf
, si
, 1, src
.mOffset
, hrirPoints
);
1890 azd
->mIrs
[1] = hrirs
.subspan(
1891 (size_t{hData
->mIrCount
}+azd
->mIndex
) * hData
->mIrSize
, hData
->mIrSize
);
1892 azd
->mDelays
[1] = AverageHrirOnset(onsetResampler
, onsetSamples
,
1893 hData
->mIrRate
, hrirPoints
, 1.0, azd
->mDelays
[1]);
1895 resampler
->process(hrirPoints
, hrir
);
1896 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0,
1900 // TODO: Since some SOFA files contain minimum phase HRIRs,
1901 // it would be beneficial to check for per-measurement delays
1902 // (when available) to reconstruct the HRTDs.
1908 if(!ReadIndexTriplet(tr
, hData
, &fi
, &ei
, &ai
))
1910 if(!TrReadOperator(tr
, "]"))
1912 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
1914 if(!azd
->mIrs
[0].empty())
1916 TrErrorAt(tr
, line
, col
, "Redefinition of source.\n");
1919 if(!TrReadOperator(tr
, "="))
1925 if(!ReadSourceRef(tr
, &src
))
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.
1932 printf("\rLoading sources... %d file%s", count
, (count
==1)?"":"s");
1935 if(!LoadSource(&src
, hData
->mIrRate
, al::span
{hrir
}.first(hData
->mIrPoints
)))
1939 if(hData
->mChannelType
== CT_STEREO
)
1941 std::array
<char,MaxIdentLen
+1> ident
{};
1942 if(!TrReadIdent(tr
, ident
))
1944 ti
= static_cast<uint
>(MatchTargetEar(ident
.data()));
1945 if(static_cast<int>(ti
) < 0)
1947 TrErrorAt(tr
, line
, col
, "Expected a target ear.\n");
1951 const auto hrirPoints
= al::span
{hrir
}.first(hData
->mIrPoints
);
1952 azd
->mIrs
[ti
] = hrirs
.subspan((ti
*size_t{hData
->mIrCount
}+azd
->mIndex
)*hData
->mIrSize
,
1954 azd
->mDelays
[ti
] = AverageHrirOnset(onsetResampler
, onsetSamples
, hData
->mIrRate
,
1955 hrirPoints
, 1.0/factor
[ti
], azd
->mDelays
[ti
]);
1957 resampler
->process(hrirPoints
, hrir
);
1958 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.subspan(irPoints
), 1.0/factor
[ti
],
1961 if(!TrIsOperator(tr
, "+"))
1963 TrReadOperator(tr
, "+");
1965 if(hData
->mChannelType
== CT_STEREO
)
1967 if(azd
->mIrs
[0].empty())
1969 TrErrorAt(tr
, line
, col
, "Missing left ear source reference(s).\n");
1972 if(azd
->mIrs
[1].empty())
1974 TrErrorAt(tr
, line
, col
, "Missing right ear source reference(s).\n");
1983 hData
->mIrRate
= outRate
;
1984 hData
->mIrPoints
= irPoints
;
1987 for(fi
= 0;fi
< hData
->mFds
.size();fi
++)
1989 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
1991 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
1993 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
1994 if(!azd
->mIrs
[0].empty())
1997 if(ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size())
2000 if(ei
>= hData
->mFds
[fi
].mEvs
.size())
2002 TrError(tr
, "Missing source references [ %d, *, * ].\n", fi
);
2005 hData
->mFds
[fi
].mEvStart
= ei
;
2006 for(;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
2008 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
2010 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
2012 if(azd
->mIrs
[0].empty())
2014 TrError(tr
, "Missing source reference [ %d, %d, %d ].\n", fi
, ei
, ai
);
2020 for(uint ti
{0};ti
< channels
;ti
++)
2022 for(fi
= 0;fi
< hData
->mFds
.size();fi
++)
2024 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
2026 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
2028 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
2029 azd
->mIrs
[ti
] = hrirs
.subspan(
2030 (ti
*size_t{hData
->mIrCount
} + azd
->mIndex
) * hData
->mIrSize
,
2042 TrError(tr
, "Errant data at end of source list.\n");
2049 bool LoadDefInput(std::istream
&istream
, const al::span
<const char> startbytes
,
2050 const std::string_view filename
, const uint fftSize
, const uint truncSize
, const uint outRate
,
2051 const ChannelModeT chanMode
, HrirDataT
*hData
)
2053 TokenReaderT tr
{istream
};
2055 TrSetup(startbytes
, filename
, &tr
);
2056 if(!ProcessMetrics(&tr
, fftSize
, truncSize
, chanMode
, hData
)
2057 || !ProcessSources(&tr
, hData
, outRate
))