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 using namespace std::string_view_literals
;
58 // Constants for accessing the token reader's ring buffer.
59 constexpr uint TRRingBits
{16};
60 constexpr uint TRRingSize
{1 << TRRingBits
};
61 constexpr uint TRRingMask
{TRRingSize
- 1};
63 // The token reader's load interval in bytes.
64 constexpr uint TRLoadSize
{TRRingSize
>> 2};
66 // Token reader state for parsing the data set definition.
68 std::istream
&mIStream
;
72 std::array
<char,TRRingSize
> mRing
{};
73 std::streamsize mIn
{};
74 std::streamsize mOut
{};
76 TokenReaderT(std::istream
&istream
) noexcept
: mIStream
{istream
} { }
77 TokenReaderT(const TokenReaderT
&) = default;
81 // The limits for the listener's head 'radius' in the data set definition.
82 constexpr double MinRadius
{0.05};
83 constexpr double MaxRadius
{0.15};
85 // The maximum number of channels that can be addressed for a WAVE file
86 // source listed in the data set definition.
87 constexpr uint MaxWaveChannels
{65535};
89 // The limits to the byte size for a binary source listed in the definition
96 // The limits to the number of significant bits for an ASCII source listed in
97 // the data set definition.
103 // The four-character-codes for RIFF/RIFX WAVE file chunks.
105 FOURCC_RIFF
= 0x46464952, // 'RIFF'
106 FOURCC_RIFX
= 0x58464952, // 'RIFX'
107 FOURCC_WAVE
= 0x45564157, // 'WAVE'
108 FOURCC_FMT
= 0x20746D66, // 'fmt '
109 FOURCC_DATA
= 0x61746164, // 'data'
110 FOURCC_LIST
= 0x5453494C, // 'LIST'
111 FOURCC_WAVL
= 0x6C766177, // 'wavl'
112 FOURCC_SLNT
= 0x746E6C73, // 'slnt'
115 // The supported wave formats.
117 WAVE_FORMAT_PCM
= 0x0001,
118 WAVE_FORMAT_IEEE_FLOAT
= 0x0003,
119 WAVE_FORMAT_EXTENSIBLE
= 0xFFFE,
129 // Source format for the references listed in the data set definition.
132 SF_ASCII
, // ASCII text file.
133 SF_BIN_LE
, // Little-endian binary file.
134 SF_BIN_BE
, // Big-endian binary file.
135 SF_WAVE
, // RIFF/RIFX WAVE file.
136 SF_SOFA
// Spatially Oriented Format for Accoustics (SOFA) file.
139 // Element types for the references listed in the data set definition.
142 ET_INT
, // Integer elements.
143 ET_FP
// Floating-point elements.
146 // Source reference state used when loading sources.
148 SourceFormatT mFormat
;
158 std::array
<char,MAX_PATH_LEN
+1> mPath
;
162 /* Whitespace is not significant. It can process tokens as identifiers, numbers
163 * (integer and floating-point), strings, and operators. Strings must be
164 * encapsulated by double-quotes and cannot span multiple lines.
167 // Setup the reader on the given file. The filename can be NULL if no error
168 // output is desired.
169 void TrSetup(const al::span
<const char> startbytes
, const std::string_view filename
,
172 std::string_view namepart
;
174 if(!filename
.empty())
176 const auto fslashpos
= filename
.rfind('/');
177 const auto bslashpos
= filename
.rfind('\\');
178 const auto slashpos
= (bslashpos
>= filename
.size()) ? fslashpos
:
179 (fslashpos
>= filename
.size()) ? bslashpos
:
180 std::max(fslashpos
, bslashpos
);
181 if(slashpos
< filename
.size())
182 namepart
= filename
.substr(slashpos
+1);
185 tr
->mName
= namepart
;
191 if(!startbytes
.empty())
193 assert(startbytes
.size() <= tr
->mRing
.size());
194 std::copy(startbytes
.cbegin(), startbytes
.cend(), tr
->mRing
.begin());
195 tr
->mIn
+= std::streamsize(startbytes
.size());
199 // Prime the reader's ring buffer, and return a result indicating that there
200 // is text to process.
201 auto TrLoad(TokenReaderT
*tr
) -> int
203 std::istream
&istream
= tr
->mIStream
;
205 auto toLoad
= std::streamsize
{TRRingSize
} - (tr
->mIn
- tr
->mOut
);
206 if(toLoad
>= TRLoadSize
&& istream
.good())
208 // Load TRLoadSize (or less if at the end of the file) per read.
211 const auto in
= tr
->mIn
&TRRingMask
;
212 std::streamsize count
{TRRingSize
- in
};
215 istream
.read(al::to_address(tr
->mRing
.begin() + in
), count
);
216 tr
->mIn
+= istream
.gcount();
217 istream
.read(tr
->mRing
.data(), toLoad
-count
);
218 tr
->mIn
+= istream
.gcount();
222 istream
.read(al::to_address(tr
->mRing
.begin() + in
), toLoad
);
223 tr
->mIn
+= istream
.gcount();
226 if(tr
->mOut
>= TRRingSize
)
228 tr
->mOut
-= TRRingSize
;
229 tr
->mIn
-= TRRingSize
;
232 if(tr
->mIn
> tr
->mOut
)
237 // Error display routine. Only displays when the base name is not NULL.
238 void TrErrorVA(const TokenReaderT
*tr
, uint line
, uint column
, const char *format
, va_list argPtr
)
240 if(tr
->mName
.empty())
242 fprintf(stderr
, "\nError (%s:%u:%u): ", tr
->mName
.c_str(), line
, column
);
243 vfprintf(stderr
, format
, argPtr
);
246 // Used to display an error at a saved line/column.
247 void TrErrorAt(const TokenReaderT
*tr
, uint line
, uint column
, const char *format
, ...)
249 /* NOLINTBEGIN(*-array-to-pointer-decay) */
251 va_start(argPtr
, format
);
252 TrErrorVA(tr
, line
, column
, format
, argPtr
);
254 /* NOLINTEND(*-array-to-pointer-decay) */
257 // Used to display an error at the current line/column.
258 void TrError(const TokenReaderT
*tr
, const char *format
, ...)
260 /* NOLINTBEGIN(*-array-to-pointer-decay) */
262 va_start(argPtr
, format
);
263 TrErrorVA(tr
, tr
->mLine
, tr
->mColumn
, format
, argPtr
);
265 /* NOLINTEND(*-array-to-pointer-decay) */
268 // Skips to the next line.
269 void TrSkipLine(TokenReaderT
*tr
)
275 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
287 // Skips to the next token.
288 auto TrSkipWhitespace(TokenReaderT
*tr
) -> int
292 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
312 // Get the line and/or column of the next token (or the end of input).
313 void TrIndication(TokenReaderT
*tr
, uint
*line
, uint
*column
)
315 TrSkipWhitespace(tr
);
316 if(line
) *line
= tr
->mLine
;
317 if(column
) *column
= tr
->mColumn
;
320 // Checks to see if a token is (likely to be) an identifier. It does not
321 // display any errors and will not proceed to the next token.
322 auto TrIsIdent(TokenReaderT
*tr
) -> int
324 if(!TrSkipWhitespace(tr
))
326 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
327 return ch
== '_' || isalpha(ch
);
331 // Checks to see if a token is the given operator. It does not display any
332 // errors and will not proceed to the next token.
333 auto TrIsOperator(TokenReaderT
*tr
, const std::string_view op
) -> int
335 if(!TrSkipWhitespace(tr
))
339 while(len
< op
.size() && out
< tr
->mIn
)
341 if(tr
->mRing
[out
&TRRingMask
] != op
[len
])
351 /* The TrRead*() routines obtain the value of a matching token type. They
352 * display type, form, and boundary errors and will proceed to the next
356 // Reads and validates an identifier token.
357 auto TrReadIdent(TokenReaderT
*tr
) -> std::string
359 auto ret
= std::string
{};
360 auto col
= tr
->mColumn
;
361 if(TrSkipWhitespace(tr
))
364 auto ch
= char{tr
->mRing
[tr
->mOut
&TRRingMask
]};
365 if(ch
== '_' || isalpha(ch
))
373 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
374 } while(ch
== '_' || std::isdigit(ch
) || std::isalpha(ch
));
379 TrErrorAt(tr
, tr
->mLine
, col
, "Expected an identifier.\n");
384 // Reads and validates (including bounds) an integer token.
385 auto TrReadInt(TokenReaderT
*tr
, const int loBound
, const int hiBound
, int *value
) -> int
387 uint col
{tr
->mColumn
};
388 if(TrSkipWhitespace(tr
))
392 std::array
<char,64+1> temp
{};
393 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
394 if(ch
== '+' || ch
== '-')
403 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
404 if(!isdigit(ch
)) break;
412 if(digis
> 0 && ch
!= '.' && !isalpha(ch
))
416 TrErrorAt(tr
, tr
->mLine
, col
, "Integer is too long.");
420 *value
= static_cast<int>(strtol(temp
.data(), nullptr, 10));
421 if(*value
< loBound
|| *value
> hiBound
)
423 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a value from %d to %d.\n", loBound
, hiBound
);
429 TrErrorAt(tr
, tr
->mLine
, col
, "Expected an integer.\n");
433 // Reads and validates (including bounds) a float token.
434 auto TrReadFloat(TokenReaderT
*tr
, const double loBound
, const double hiBound
, double *value
) -> int
436 uint col
{tr
->mColumn
};
437 if(TrSkipWhitespace(tr
))
440 std::array
<char,64+1> temp
{};
442 char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]};
443 if(ch
== '+' || ch
== '-')
453 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
454 if(!isdigit(ch
)) break;
470 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
471 if(!isdigit(ch
)) break;
480 if(ch
== 'E' || ch
== 'e')
487 if(ch
== '+' || ch
== '-')
496 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
497 if(!isdigit(ch
)) break;
506 if(digis
> 0 && ch
!= '.' && !isalpha(ch
))
510 TrErrorAt(tr
, tr
->mLine
, col
, "Float is too long.");
514 *value
= strtod(temp
.data(), nullptr);
515 if(*value
< loBound
|| *value
> hiBound
)
517 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a value from %f to %f.\n", loBound
, hiBound
);
526 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a float.\n");
530 // Reads and validates a string token.
531 auto TrReadString(TokenReaderT
*tr
, const al::span
<char> text
) -> int
533 assert(!text
.empty());
534 const size_t maxLen
{text
.size()-1};
536 uint col
{tr
->mColumn
};
537 if(TrSkipWhitespace(tr
))
540 if(char ch
{tr
->mRing
[tr
->mOut
&TRRingMask
]}; ch
== '\"')
546 ch
= tr
->mRing
[tr
->mOut
&TRRingMask
];
552 TrErrorAt(tr
, tr
->mLine
, col
, "Unterminated string at end of line.\n");
561 tr
->mColumn
+= static_cast<uint
>(1 + len
);
562 TrErrorAt(tr
, tr
->mLine
, col
, "Unterminated string at end of input.\n");
565 tr
->mColumn
+= static_cast<uint
>(2 + len
);
568 TrErrorAt(tr
, tr
->mLine
, col
, "String is too long.\n");
575 TrErrorAt(tr
, tr
->mLine
, col
, "Expected a string.\n");
579 // Reads and validates the given operator.
580 auto TrReadOperator(TokenReaderT
*tr
, const std::string_view op
) -> int
582 uint col
{tr
->mColumn
};
583 if(TrSkipWhitespace(tr
))
587 while(len
< op
.size() && TrLoad(tr
))
589 if(tr
->mRing
[tr
->mOut
&TRRingMask
] != op
[len
])
594 tr
->mColumn
+= static_cast<uint
>(len
);
598 TrErrorAt(tr
, tr
->mLine
, col
, "Expected '%s' operator.\n", op
);
603 /*************************
604 *** File source input ***
605 *************************/
607 // Read a binary value of the specified byte order and byte size from a file,
608 // storing it as a 32-bit unsigned integer.
609 auto ReadBin4(std::istream
&istream
, const char *filename
, const ByteOrderT order
,
610 const uint bytes
, uint32_t *out
) -> int
612 std::array
<uint8_t,4> in
{};
613 istream
.read(reinterpret_cast<char*>(in
.data()), static_cast<int>(bytes
));
614 if(istream
.gcount() != bytes
)
616 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
623 for(uint i
= 0;i
< bytes
;i
++)
624 accum
= (accum
<<8) | in
[bytes
- i
- 1];
627 for(uint i
= 0;i
< bytes
;i
++)
628 accum
= (accum
<<8) | in
[i
];
637 // Read a binary value of the specified byte order from a file, storing it as
638 // a 64-bit unsigned integer.
639 auto ReadBin8(std::istream
&istream
, const char *filename
, const ByteOrderT order
, uint64_t *out
) -> int
641 std::array
<uint8_t,8> in
{};
642 istream
.read(reinterpret_cast<char*>(in
.data()), 8);
643 if(istream
.gcount() != 8)
645 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
653 for(uint i
{0};i
< 8;++i
)
654 accum
= (accum
<<8) | in
[8 - i
- 1];
657 for(uint i
{0};i
< 8;++i
)
658 accum
= (accum
<<8) | in
[i
];
667 /* Read a binary value of the specified type, byte order, and byte size from
668 * a file, converting it to a double. For integer types, the significant
669 * bits are used to normalize the result. The sign of bits determines
670 * whether they are padded toward the MSB (negative) or LSB (positive).
671 * Floating-point types are not normalized.
673 auto ReadBinAsDouble(std::istream
&istream
, const char *filename
, const ByteOrderT order
,
674 const ElementTypeT type
, const uint bytes
, const int bits
, double *out
) -> int
680 if(!ReadBin8(istream
, filename
, order
, &val
))
683 *out
= al::bit_cast
<double>(val
);
688 if(!ReadBin4(istream
, filename
, order
, bytes
, &val
))
691 *out
= al::bit_cast
<float>(val
);
695 val
>>= (8*bytes
) - (static_cast<uint
>(bits
));
697 val
&= (0xFFFFFFFF >> (32+bits
));
699 if(val
&static_cast<uint
>(1<<(std::abs(bits
)-1)))
700 val
|= (0xFFFFFFFF << std::abs(bits
));
701 *out
= static_cast<int32_t>(val
) / static_cast<double>(1<<(std::abs(bits
)-1));
707 /* Read an ascii value of the specified type from a file, converting it to a
708 * double. For integer types, the significant bits are used to normalize the
709 * result. The sign of the bits should always be positive. This also skips
710 * up to one separator character before the element itself.
712 auto ReadAsciiAsDouble(TokenReaderT
*tr
, const char *filename
, const ElementTypeT type
,
713 const uint bits
, double *out
) -> int
715 if(TrIsOperator(tr
, ","))
716 TrReadOperator(tr
, ",");
717 else if(TrIsOperator(tr
, ":"))
718 TrReadOperator(tr
, ":");
719 else if(TrIsOperator(tr
, ";"))
720 TrReadOperator(tr
, ";");
721 else if(TrIsOperator(tr
, "|"))
722 TrReadOperator(tr
, "|");
726 if(!TrReadFloat(tr
, -std::numeric_limits
<double>::infinity(),
727 std::numeric_limits
<double>::infinity(), out
))
729 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
736 if(!TrReadInt(tr
, -(1<<(bits
-1)), (1<<(bits
-1))-1, &v
))
738 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", filename
);
741 *out
= v
/ static_cast<double>((1<<(bits
-1))-1);
746 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
747 // the source parameters and data set metrics.
748 auto ReadWaveFormat(std::istream
&istream
, const ByteOrderT order
, const uint hrirRate
,
749 SourceRefT
*src
) -> int
751 uint32_t fourCC
, chunkSize
;
752 uint32_t format
, channels
, rate
, dummy
, block
, size
, bits
;
757 istream
.seekg(static_cast<int>(chunkSize
), std::ios::cur
);
758 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
759 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
761 } while(fourCC
!= FOURCC_FMT
);
762 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &format
)
763 || !ReadBin4(istream
, src
->mPath
.data(), order
, 2, &channels
)
764 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &rate
)
765 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &dummy
)
766 || !ReadBin4(istream
, src
->mPath
.data(), order
, 2, &block
))
771 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &size
))
779 if(format
== WAVE_FORMAT_EXTENSIBLE
)
781 istream
.seekg(2, std::ios::cur
);
782 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &bits
))
786 istream
.seekg(4, std::ios::cur
);
787 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 2, &format
))
789 istream
.seekg(static_cast<int>(chunkSize
- 26), std::ios::cur
);
795 istream
.seekg(static_cast<int>(chunkSize
- 16), std::ios::cur
);
797 istream
.seekg(static_cast<int>(chunkSize
- 14), std::ios::cur
);
799 if(format
!= WAVE_FORMAT_PCM
&& format
!= WAVE_FORMAT_IEEE_FLOAT
)
801 fprintf(stderr
, "\nError: Unsupported WAVE format in file '%s'.\n", src
->mPath
.data());
804 if(src
->mChannel
>= channels
)
806 fprintf(stderr
, "\nError: Missing source channel in WAVE file '%s'.\n", src
->mPath
.data());
811 fprintf(stderr
, "\nError: Mismatched source sample rate in WAVE file '%s'.\n",
815 if(format
== WAVE_FORMAT_PCM
)
817 if(size
< 2 || size
> 4)
819 fprintf(stderr
, "\nError: Unsupported sample size in WAVE file '%s'.\n",
823 if(bits
< 16 || bits
> (8*size
))
825 fprintf(stderr
, "\nError: Bad significant bits in WAVE file '%s'.\n",
833 if(size
!= 4 && size
!= 8)
835 fprintf(stderr
, "\nError: Unsupported sample size in WAVE file '%s'.\n",
842 src
->mBits
= static_cast<int>(bits
);
843 src
->mSkip
= channels
;
847 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
848 auto ReadWaveData(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
849 const al::span
<double> hrir
) -> int
851 auto pre
= static_cast<int>(src
->mSize
* src
->mChannel
);
852 auto post
= static_cast<int>(src
->mSize
* (src
->mSkip
- src
->mChannel
- 1));
854 for(size_t i
{0};i
< hrir
.size();++i
)
858 istream
.seekg(skip
, std::ios::cur
);
859 if(!ReadBinAsDouble(istream
, src
->mPath
.data(), order
, src
->mType
, src
->mSize
, src
->mBits
,
865 istream
.seekg(skip
, std::ios::cur
);
869 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
871 auto ReadWaveList(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
872 const al::span
<double> hrir
) -> int
874 uint32_t fourCC
, chunkSize
, listSize
, count
;
875 uint block
, skip
, offset
, i
;
880 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
881 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
884 if(fourCC
== FOURCC_DATA
)
886 block
= src
->mSize
* src
->mSkip
;
887 count
= chunkSize
/ block
;
888 if(count
< (src
->mOffset
+ hrir
.size()))
890 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", src
->mPath
.data());
893 using off_type
= std::istream::off_type
;
894 istream
.seekg(off_type(src
->mOffset
) * off_type(block
), std::ios::cur
);
895 if(!ReadWaveData(istream
, src
, order
, hrir
))
899 if(fourCC
== FOURCC_LIST
)
901 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
))
904 if(fourCC
== FOURCC_WAVL
)
908 istream
.seekg(static_cast<long>(chunkSize
), std::ios::cur
);
910 listSize
= chunkSize
;
911 block
= src
->mSize
* src
->mSkip
;
915 while(offset
< hrir
.size() && listSize
> 8)
917 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
918 || !ReadBin4(istream
, src
->mPath
.data(), order
, 4, &chunkSize
))
920 listSize
-= 8 + chunkSize
;
921 if(fourCC
== FOURCC_DATA
)
923 count
= chunkSize
/ block
;
926 using off_type
= std::istream::off_type
;
927 istream
.seekg(off_type(skip
) * off_type(block
), std::ios::cur
);
928 chunkSize
-= skip
* block
;
931 if(count
> (hrir
.size() - offset
))
932 count
= static_cast<uint
>(hrir
.size() - offset
);
933 if(!ReadWaveData(istream
, src
, order
, hrir
.subspan(offset
, count
)))
935 chunkSize
-= count
* block
;
937 lastSample
= hrir
[offset
- 1];
945 else if(fourCC
== FOURCC_SLNT
)
947 if(!ReadBin4(istream
, src
->mPath
.data(), order
, 4, &count
))
954 if(count
> (hrir
.size() - offset
))
955 count
= static_cast<uint
>(hrir
.size() - offset
);
956 for(i
= 0; i
< count
; i
++)
957 hrir
[offset
+ i
] = lastSample
;
967 istream
.seekg(static_cast<long>(chunkSize
), std::ios::cur
);
969 if(offset
< hrir
.size())
971 fprintf(stderr
, "\nError: Bad read from file '%s'.\n", src
->mPath
.data());
977 // Load a source HRIR from an ASCII text file containing a list of elements
978 // separated by whitespace or common list operators (',', ';', ':', '|').
979 auto LoadAsciiSource(std::istream
&istream
, const SourceRefT
*src
, const al::span
<double> hrir
) -> int
981 TokenReaderT tr
{istream
};
983 TrSetup({}, {}, &tr
);
984 for(uint i
{0};i
< src
->mOffset
;++i
)
987 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
, static_cast<uint
>(src
->mBits
),
991 for(size_t i
{0};i
< hrir
.size();++i
)
993 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
, static_cast<uint
>(src
->mBits
),
996 for(uint j
{0};j
< src
->mSkip
;++j
)
999 if(!ReadAsciiAsDouble(&tr
, src
->mPath
.data(), src
->mType
,
1000 static_cast<uint
>(src
->mBits
), &dummy
))
1007 // Load a source HRIR from a binary file.
1008 auto LoadBinarySource(std::istream
&istream
, const SourceRefT
*src
, const ByteOrderT order
,
1009 const al::span
<double> hrir
) -> int
1011 istream
.seekg(static_cast<long>(src
->mOffset
), std::ios::beg
);
1012 for(size_t i
{0};i
< hrir
.size();++i
)
1014 if(!ReadBinAsDouble(istream
, src
->mPath
.data(), order
, src
->mType
, src
->mSize
, src
->mBits
,
1018 istream
.seekg(static_cast<long>(src
->mSkip
), std::ios::cur
);
1023 // Load a source HRIR from a RIFF/RIFX WAVE file.
1024 auto LoadWaveSource(std::istream
&istream
, SourceRefT
*src
, const uint hrirRate
,
1025 const al::span
<double> hrir
) -> int
1027 uint32_t fourCC
, dummy
;
1030 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
)
1031 || !ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &dummy
))
1033 if(fourCC
== FOURCC_RIFF
)
1035 else if(fourCC
== FOURCC_RIFX
)
1039 fprintf(stderr
, "\nError: No RIFF/RIFX chunk in file '%s'.\n", src
->mPath
.data());
1043 if(!ReadBin4(istream
, src
->mPath
.data(), BO_LITTLE
, 4, &fourCC
))
1045 if(fourCC
!= FOURCC_WAVE
)
1047 fprintf(stderr
, "\nError: Not a RIFF/RIFX WAVE file '%s'.\n", src
->mPath
.data());
1050 if(!ReadWaveFormat(istream
, order
, hrirRate
, src
))
1052 if(!ReadWaveList(istream
, src
, order
, hrir
))
1058 struct SofaEasyDeleter
{
1059 void operator()(gsl::owner
<MYSOFA_EASY
*> sofa
)
1061 if(sofa
->neighborhood
) mysofa_neighborhood_free(sofa
->neighborhood
);
1062 if(sofa
->lookup
) mysofa_lookup_free(sofa
->lookup
);
1063 if(sofa
->hrtf
) mysofa_free(sofa
->hrtf
);
1067 using SofaEasyPtr
= std::unique_ptr
<MYSOFA_EASY
,SofaEasyDeleter
>;
1069 struct SofaCacheEntry
{
1074 std::vector
<SofaCacheEntry
> gSofaCache
;
1076 // Load a Spatially Oriented Format for Accoustics (SOFA) file.
1077 auto LoadSofaFile(SourceRefT
*src
, const uint hrirRate
, const uint n
) -> MYSOFA_EASY
*
1079 const std::string_view srcname
{src
->mPath
.data()};
1080 auto iter
= std::find_if(gSofaCache
.begin(), gSofaCache
.end(),
1081 [srcname
,hrirRate
](SofaCacheEntry
&entry
) -> bool
1082 { return entry
.mName
== srcname
&& entry
.mSampleRate
== hrirRate
; });
1083 if(iter
!= gSofaCache
.end()) return iter
->mSofa
.get();
1085 SofaEasyPtr sofa
{new(std::nothrow
) MYSOFA_EASY
{}};
1088 fprintf(stderr
, "\nError: Out of memory.\n");
1091 sofa
->lookup
= nullptr;
1092 sofa
->neighborhood
= nullptr;
1095 sofa
->hrtf
= mysofa_load(src
->mPath
.data(), &err
);
1098 fprintf(stderr
, "\nError: Could not load source file '%s': %s (%d).\n",
1099 src
->mPath
.data(), SofaErrorStr(err
), err
);
1102 /* NOTE: Some valid SOFA files are failing this check. */
1103 err
= mysofa_check(sofa
->hrtf
);
1104 if(err
!= MYSOFA_OK
)
1105 fprintf(stderr
, "\nWarning: Supposedly malformed source file '%s': %s (%d).\n",
1106 src
->mPath
.data(), SofaErrorStr(err
), err
);
1107 if((src
->mOffset
+ n
) > sofa
->hrtf
->N
)
1109 fprintf(stderr
, "\nError: Not enough samples in SOFA file '%s'.\n", src
->mPath
.data());
1112 if(src
->mChannel
>= sofa
->hrtf
->R
)
1114 fprintf(stderr
, "\nError: Missing source receiver in SOFA file '%s'.\n",src
->mPath
.data());
1117 mysofa_tocartesian(sofa
->hrtf
);
1118 sofa
->lookup
= mysofa_lookup_init(sofa
->hrtf
);
1119 if(sofa
->lookup
== nullptr)
1121 fprintf(stderr
, "\nError: Out of memory.\n");
1124 gSofaCache
.emplace_back(SofaCacheEntry
{std::string
{srcname
}, hrirRate
, std::move(sofa
)});
1125 return gSofaCache
.back().mSofa
.get();
1128 // Copies the HRIR data from a particular SOFA measurement.
1129 void ExtractSofaHrir(const MYSOFA_HRTF
*hrtf
, const size_t index
, const size_t channel
,
1130 const size_t offset
, const al::span
<double> hrir
)
1132 const auto irValues
= al::span
{hrtf
->DataIR
.values
, hrtf
->DataIR
.elements
}
1133 .subspan((index
*hrtf
->R
+ channel
)*hrtf
->N
+ offset
);
1134 std::copy_n(irValues
.cbegin(), hrir
.size(), hrir
.begin());
1137 // Load a source HRIR from a Spatially Oriented Format for Accoustics (SOFA)
1139 auto LoadSofaSource(SourceRefT
*src
, const uint hrirRate
, const al::span
<double> hrir
) -> int
1141 MYSOFA_EASY
*sofa
{LoadSofaFile(src
, hrirRate
, static_cast<uint
>(hrir
.size()))};
1142 if(sofa
== nullptr) return 0;
1144 /* NOTE: At some point it may be beneficial or necessary to consider the
1145 various coordinate systems, listener/source orientations, and
1146 directional vectors defined in the SOFA file.
1149 static_cast<float>(src
->mAzimuth
),
1150 static_cast<float>(src
->mElevation
),
1151 static_cast<float>(src
->mRadius
)
1153 mysofa_s2c(target
.data());
1155 int nearest
{mysofa_lookup(sofa
->lookup
, target
.data())};
1158 fprintf(stderr
, "\nError: Lookup failed in source file '%s'.\n", src
->mPath
.data());
1162 al::span
<float,3> coords
= al::span
{sofa
->hrtf
->SourcePosition
.values
, sofa
->hrtf
->M
*3_uz
}
1163 .subspan(static_cast<uint
>(nearest
)*3_uz
).first
<3>();
1164 if(std::abs(coords
[0] - target
[0]) > 0.001 || std::abs(coords
[1] - target
[1]) > 0.001
1165 || std::abs(coords
[2] - target
[2]) > 0.001)
1167 fprintf(stderr
, "\nError: No impulse response at coordinates (%.3fr, %.1fev, %.1faz) in file '%s'.\n",
1168 src
->mRadius
, src
->mElevation
, src
->mAzimuth
, src
->mPath
.data());
1169 target
[0] = coords
[0];
1170 target
[1] = coords
[1];
1171 target
[2] = coords
[2];
1172 mysofa_c2s(target
.data());
1173 fprintf(stderr
, " Nearest candidate at (%.3fr, %.1fev, %.1faz).\n", target
[2],
1174 target
[1], target
[0]);
1178 ExtractSofaHrir(sofa
->hrtf
, static_cast<uint
>(nearest
), src
->mChannel
, src
->mOffset
, hrir
);
1183 // Load a source HRIR from a supported file type.
1184 auto LoadSource(SourceRefT
*src
, const uint hrirRate
, const al::span
<double> hrir
) -> int
1186 std::unique_ptr
<std::istream
> istream
;
1187 if(src
->mFormat
!= SF_SOFA
)
1189 if(src
->mFormat
== SF_ASCII
)
1190 istream
= std::make_unique
<std::ifstream
>(std::filesystem::u8path(src
->mPath
.data()));
1192 istream
= std::make_unique
<std::ifstream
>(std::filesystem::u8path(src
->mPath
.data()),
1194 if(!istream
->good())
1196 fprintf(stderr
, "\nError: Could not open source file '%s'.\n", src
->mPath
.data());
1201 switch(src
->mFormat
)
1203 case SF_ASCII
: return LoadAsciiSource(*istream
, src
, hrir
);
1204 case SF_BIN_LE
: return LoadBinarySource(*istream
, src
, BO_LITTLE
, hrir
);
1205 case SF_BIN_BE
: return LoadBinarySource(*istream
, src
, BO_BIG
, hrir
);
1206 case SF_WAVE
: return LoadWaveSource(*istream
, src
, hrirRate
, hrir
);
1207 case SF_SOFA
: return LoadSofaSource(src
, hrirRate
, hrir
);
1208 case SF_NONE
: break;
1214 // Match the channel type from a given identifier.
1215 auto MatchChannelType(const std::string_view ident
) -> ChannelTypeT
1217 if(al::case_compare(ident
, "mono"sv
) == 0)
1219 if(al::case_compare(ident
, "stereo"sv
) == 0)
1225 // Process the data set definition to read and validate the data set metrics.
1226 auto ProcessMetrics(TokenReaderT
*tr
, const uint fftSize
, const uint truncSize
,
1227 const ChannelModeT chanMode
, HrirDataT
*hData
) -> int
1229 int hasRate
= 0, hasType
= 0, hasPoints
= 0, hasRadius
= 0;
1230 int hasDistance
= 0, hasAzimuths
= 0;
1235 std::array
<double,MAX_FD_COUNT
> distances
{};
1237 std::array
<uint
,MAX_FD_COUNT
> evCounts
{};
1238 auto azCounts
= std::vector
<std::array
<uint
,MAX_EV_COUNT
>>(MAX_FD_COUNT
);
1239 for(auto &azs
: azCounts
) azs
.fill(0u);
1241 TrIndication(tr
, &line
, &col
);
1242 while(TrIsIdent(tr
))
1244 TrIndication(tr
, &line
, &col
);
1245 const auto ident
= TrReadIdent(tr
);
1248 if(al::case_compare(ident
, "rate"sv
) == 0)
1252 TrErrorAt(tr
, line
, col
, "Redefinition of 'rate'.\n");
1255 if(!TrReadOperator(tr
, "="))
1257 if(!TrReadInt(tr
, MIN_RATE
, MAX_RATE
, &intVal
))
1259 hData
->mIrRate
= static_cast<uint
>(intVal
);
1262 else if(al::case_compare(ident
, "type"sv
) == 0)
1266 TrErrorAt(tr
, line
, col
, "Redefinition of 'type'.\n");
1269 if(!TrReadOperator(tr
, "="))
1272 const auto type
= TrReadIdent(tr
);
1275 hData
->mChannelType
= MatchChannelType(type
);
1276 if(hData
->mChannelType
== CT_NONE
)
1278 TrErrorAt(tr
, line
, col
, "Expected a channel type.\n");
1281 if(hData
->mChannelType
== CT_STEREO
)
1283 if(chanMode
== CM_ForceMono
)
1284 hData
->mChannelType
= CT_MONO
;
1288 else if(al::case_compare(ident
, "points"sv
) == 0)
1292 TrErrorAt(tr
, line
, col
, "Redefinition of 'points'.\n");
1295 if(!TrReadOperator(tr
, "="))
1297 TrIndication(tr
, &line
, &col
);
1298 if(!TrReadInt(tr
, MIN_POINTS
, MAX_POINTS
, &intVal
))
1300 points
= static_cast<uint
>(intVal
);
1301 if(fftSize
> 0 && points
> fftSize
)
1303 TrErrorAt(tr
, line
, col
, "Value exceeds the overridden FFT size.\n");
1306 if(points
< truncSize
)
1308 TrErrorAt(tr
, line
, col
, "Value is below the truncation size.\n");
1311 hData
->mIrPoints
= points
;
1312 hData
->mFftSize
= fftSize
;
1313 hData
->mIrSize
= 1 + (fftSize
/ 2);
1314 if(points
> hData
->mIrSize
)
1315 hData
->mIrSize
= points
;
1318 else if(al::case_compare(ident
, "radius"sv
) == 0)
1322 TrErrorAt(tr
, line
, col
, "Redefinition of 'radius'.\n");
1325 if(!TrReadOperator(tr
, "="))
1327 if(!TrReadFloat(tr
, MinRadius
, MaxRadius
, &fpVal
))
1329 hData
->mRadius
= fpVal
;
1332 else if(al::case_compare(ident
, "distance"sv
) == 0)
1334 auto count
= uint
{0};
1338 TrErrorAt(tr
, line
, col
, "Redefinition of 'distance'.\n");
1341 if(!TrReadOperator(tr
, "="))
1346 if(!TrReadFloat(tr
, MIN_DISTANCE
, MAX_DISTANCE
, &fpVal
))
1348 if(count
> 0 && fpVal
<= distances
[count
- 1])
1350 TrError(tr
, "Distances are not ascending.\n");
1353 distances
[count
++] = fpVal
;
1354 if(!TrIsOperator(tr
, ","))
1356 if(count
>= MAX_FD_COUNT
)
1358 TrError(tr
, "Exceeded the maximum of %d fields.\n", MAX_FD_COUNT
);
1361 TrReadOperator(tr
, ",");
1363 if(fdCount
!= 0 && count
!= fdCount
)
1365 TrError(tr
, "Did not match the specified number of %d fields.\n", fdCount
);
1371 else if(al::case_compare(ident
, "azimuths"sv
) == 0)
1373 auto count
= uint
{0};
1377 TrErrorAt(tr
, line
, col
, "Redefinition of 'azimuths'.\n");
1380 if(!TrReadOperator(tr
, "="))
1386 if(!TrReadInt(tr
, MIN_AZ_COUNT
, MAX_AZ_COUNT
, &intVal
))
1388 azCounts
[count
][evCounts
[count
]++] = static_cast<uint
>(intVal
);
1389 if(TrIsOperator(tr
, ","))
1391 if(evCounts
[count
] >= MAX_EV_COUNT
)
1393 TrError(tr
, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT
);
1396 TrReadOperator(tr
, ",");
1400 if(evCounts
[count
] < MIN_EV_COUNT
)
1402 TrErrorAt(tr
, line
, col
, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT
);
1405 if(azCounts
[count
][0] != 1 || azCounts
[count
][evCounts
[count
] - 1] != 1)
1407 TrError(tr
, "Poles are not singular for field %d.\n", count
- 1);
1411 if(!TrIsOperator(tr
, ";"))
1414 if(count
>= MAX_FD_COUNT
)
1416 TrError(tr
, "Exceeded the maximum number of %d fields.\n", MAX_FD_COUNT
);
1419 evCounts
[count
] = 0;
1420 TrReadOperator(tr
, ";");
1423 if(fdCount
!= 0 && count
!= fdCount
)
1425 TrError(tr
, "Did not match the specified number of %d fields.\n", fdCount
);
1433 TrErrorAt(tr
, line
, col
, "Expected a metric name.\n");
1436 TrSkipWhitespace(tr
);
1438 if(!(hasRate
&& hasPoints
&& hasRadius
&& hasDistance
&& hasAzimuths
))
1440 TrErrorAt(tr
, line
, col
, "Expected a metric name.\n");
1443 if(distances
[0] < hData
->mRadius
)
1445 TrError(tr
, "Distance cannot start below head radius.\n");
1448 if(hData
->mChannelType
== CT_NONE
)
1449 hData
->mChannelType
= CT_MONO
;
1450 const auto azs
= al::span
{azCounts
}.first
<MAX_FD_COUNT
>();
1451 if(!PrepareHrirData(al::span
{distances
}.first(fdCount
), evCounts
, azs
, hData
))
1453 fprintf(stderr
, "Error: Out of memory.\n");
1459 // Parse an index triplet from the data set definition.
1460 auto ReadIndexTriplet(TokenReaderT
*tr
, const HrirDataT
*hData
, uint
*fi
, uint
*ei
, uint
*ai
)->int
1464 if(hData
->mFds
.size() > 1)
1466 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
.size()-1), &intVal
))
1468 *fi
= static_cast<uint
>(intVal
);
1469 if(!TrReadOperator(tr
, ","))
1476 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
[*fi
].mEvs
.size()-1), &intVal
))
1478 *ei
= static_cast<uint
>(intVal
);
1479 if(!TrReadOperator(tr
, ","))
1481 if(!TrReadInt(tr
, 0, static_cast<int>(hData
->mFds
[*fi
].mEvs
[*ei
].mAzs
.size()-1), &intVal
))
1483 *ai
= static_cast<uint
>(intVal
);
1487 // Match the source format from a given identifier.
1488 auto MatchSourceFormat(const std::string_view ident
) -> SourceFormatT
1490 if(al::case_compare(ident
, "ascii"sv
) == 0)
1492 if(al::case_compare(ident
, "bin_le"sv
) == 0)
1494 if(al::case_compare(ident
, "bin_be"sv
) == 0)
1496 if(al::case_compare(ident
, "wave"sv
) == 0)
1498 if(al::case_compare(ident
, "sofa"sv
) == 0)
1503 // Match the source element type from a given identifier.
1504 auto MatchElementType(const std::string_view ident
) -> ElementTypeT
1506 if(al::case_compare(ident
, "int"sv
) == 0)
1508 if(al::case_compare(ident
, "fp"sv
) == 0)
1513 // Parse and validate a source reference from the data set definition.
1514 auto ReadSourceRef(TokenReaderT
*tr
, SourceRefT
*src
) -> int
1520 TrIndication(tr
, &line
, &col
);
1521 auto ident
= TrReadIdent(tr
);
1524 src
->mFormat
= MatchSourceFormat(ident
);
1525 if(src
->mFormat
== SF_NONE
)
1527 TrErrorAt(tr
, line
, col
, "Expected a source format.\n");
1530 if(!TrReadOperator(tr
, "("))
1532 if(src
->mFormat
== SF_SOFA
)
1534 if(!TrReadFloat(tr
, MIN_DISTANCE
, MAX_DISTANCE
, &fpVal
))
1536 src
->mRadius
= fpVal
;
1537 if(!TrReadOperator(tr
, ","))
1539 if(!TrReadFloat(tr
, -90.0, 90.0, &fpVal
))
1541 src
->mElevation
= fpVal
;
1542 if(!TrReadOperator(tr
, ","))
1544 if(!TrReadFloat(tr
, -360.0, 360.0, &fpVal
))
1546 src
->mAzimuth
= fpVal
;
1547 if(!TrReadOperator(tr
, ":"))
1549 if(!TrReadInt(tr
, 0, MaxWaveChannels
, &intVal
))
1551 src
->mType
= ET_NONE
;
1554 src
->mChannel
= static_cast<uint
>(intVal
);
1557 else if(src
->mFormat
== SF_WAVE
)
1559 if(!TrReadInt(tr
, 0, MaxWaveChannels
, &intVal
))
1561 src
->mType
= ET_NONE
;
1564 src
->mChannel
= static_cast<uint
>(intVal
);
1569 TrIndication(tr
, &line
, &col
);
1570 ident
= TrReadIdent(tr
);
1573 src
->mType
= MatchElementType(ident
);
1574 if(src
->mType
== ET_NONE
)
1576 TrErrorAt(tr
, line
, col
, "Expected a source element type.\n");
1579 if(src
->mFormat
== SF_BIN_LE
|| src
->mFormat
== SF_BIN_BE
)
1581 if(!TrReadOperator(tr
, ","))
1583 if(src
->mType
== ET_INT
)
1585 if(!TrReadInt(tr
, MinBinSize
, MaxBinSize
, &intVal
))
1587 src
->mSize
= static_cast<uint
>(intVal
);
1588 if(!TrIsOperator(tr
, ","))
1589 src
->mBits
= static_cast<int>(8*src
->mSize
);
1592 TrReadOperator(tr
, ",");
1593 TrIndication(tr
, &line
, &col
);
1594 if(!TrReadInt(tr
, -2147483647-1, 2147483647, &intVal
))
1596 if(std::abs(intVal
) < int{MinBinSize
}*8 || static_cast<uint
>(std::abs(intVal
)) > (8*src
->mSize
))
1598 TrErrorAt(tr
, line
, col
, "Expected a value of (+/-) %d to %d.\n", MinBinSize
*8, 8*src
->mSize
);
1601 src
->mBits
= intVal
;
1606 TrIndication(tr
, &line
, &col
);
1607 if(!TrReadInt(tr
, -2147483647-1, 2147483647, &intVal
))
1609 if(intVal
!= 4 && intVal
!= 8)
1611 TrErrorAt(tr
, line
, col
, "Expected a value of 4 or 8.\n");
1614 src
->mSize
= static_cast<uint
>(intVal
);
1618 else if(src
->mFormat
== SF_ASCII
&& src
->mType
== ET_INT
)
1620 if(!TrReadOperator(tr
, ","))
1622 if(!TrReadInt(tr
, MinASCIIBits
, MaxASCIIBits
, &intVal
))
1625 src
->mBits
= intVal
;
1633 if(!TrIsOperator(tr
, ";"))
1637 TrReadOperator(tr
, ";");
1638 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1640 src
->mSkip
= static_cast<uint
>(intVal
);
1643 if(!TrReadOperator(tr
, ")"))
1645 if(TrIsOperator(tr
, "@"))
1647 TrReadOperator(tr
, "@");
1648 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1650 src
->mOffset
= static_cast<uint
>(intVal
);
1654 if(!TrReadOperator(tr
, ":"))
1656 if(!TrReadString(tr
, src
->mPath
))
1661 // Parse and validate a SOFA source reference from the data set definition.
1662 auto ReadSofaRef(TokenReaderT
*tr
, SourceRefT
*src
) -> int
1667 TrIndication(tr
, &line
, &col
);
1668 const auto ident
= TrReadIdent(tr
);
1671 src
->mFormat
= MatchSourceFormat(ident
);
1672 if(src
->mFormat
!= SF_SOFA
)
1674 TrErrorAt(tr
, line
, col
, "Expected the SOFA source format.\n");
1678 src
->mType
= ET_NONE
;
1684 if(TrIsOperator(tr
, "@"))
1686 TrReadOperator(tr
, "@");
1687 if(!TrReadInt(tr
, 0, 0x7FFFFFFF, &intVal
))
1689 src
->mOffset
= static_cast<uint
>(intVal
);
1693 if(!TrReadOperator(tr
, ":"))
1695 if(!TrReadString(tr
, src
->mPath
))
1700 // Match the target ear (index) from a given identifier.
1701 auto MatchTargetEar(const std::string_view ident
) -> std::optional
<uint8_t>
1703 if(al::case_compare(ident
, "left"sv
) == 0)
1705 if(al::case_compare(ident
, "right"sv
) == 0)
1707 return std::nullopt
;
1710 // Calculate the onset time of an HRIR and average it with any existing
1711 // timing for its field, elevation, azimuth, and ear.
1712 constexpr int OnsetRateMultiple
{10};
1713 auto AverageHrirOnset(PPhaseResampler
&rs
, al::span
<double> upsampled
, const uint rate
,
1714 const al::span
<const double> hrir
, const double f
, const double onset
) -> double
1716 rs
.process(hrir
, upsampled
);
1718 auto abs_lt
= [](const double lhs
, const double rhs
) -> bool
1719 { return std::abs(lhs
) < std::abs(rhs
); };
1720 auto iter
= std::max_element(upsampled
.cbegin(), upsampled
.cend(), abs_lt
);
1721 return Lerp(onset
, static_cast<double>(std::distance(upsampled
.cbegin(), iter
))/(10*rate
), f
);
1724 // Calculate the magnitude response of an HRIR and average it with any
1725 // existing responses for its field, elevation, azimuth, and ear.
1726 void AverageHrirMagnitude(const uint fftSize
, const al::span
<const double> hrir
, const double f
,
1727 const al::span
<double> mag
)
1729 const uint m
{1 + (fftSize
/2)};
1730 std::vector
<complex_d
> h(fftSize
);
1731 std::vector
<double> r(m
);
1733 auto hiter
= std::copy(hrir
.cbegin(), hrir
.cend(), h
.begin());
1734 std::fill(hiter
, h
.end(), 0.0);
1736 MagnitudeResponse(h
, r
);
1737 for(uint i
{0};i
< m
;++i
)
1738 mag
[i
] = Lerp(mag
[i
], r
[i
], f
);
1741 // Process the list of sources in the data set definition.
1742 auto ProcessSources(TokenReaderT
*tr
, HrirDataT
*hData
, const uint outRate
) -> int
1744 const uint channels
{(hData
->mChannelType
== CT_STEREO
) ? 2u : 1u};
1745 hData
->mHrirsBase
.resize(size_t{channels
} * hData
->mIrCount
* hData
->mIrSize
);
1746 const auto hrirs
= al::span
<double>{hData
->mHrirsBase
};
1747 auto hrir
= std::vector
<double>(hData
->mIrSize
);
1748 uint line
, col
, fi
, ei
, ai
;
1750 std::vector
<double> onsetSamples(size_t{OnsetRateMultiple
} * hData
->mIrPoints
);
1751 PPhaseResampler onsetResampler
;
1752 onsetResampler
.init(hData
->mIrRate
, OnsetRateMultiple
*hData
->mIrRate
);
1754 std::optional
<PPhaseResampler
> resampler
;
1755 if(outRate
&& outRate
!= hData
->mIrRate
)
1756 resampler
.emplace().init(hData
->mIrRate
, outRate
);
1757 const double rateScale
{outRate
? static_cast<double>(outRate
) / hData
->mIrRate
: 1.0};
1758 const uint irPoints
{outRate
1759 ? std::min(static_cast<uint
>(std::ceil(hData
->mIrPoints
*rateScale
)), hData
->mIrPoints
)
1760 : hData
->mIrPoints
};
1762 printf("Loading sources...");
1765 while(TrIsOperator(tr
, "["))
1767 std::array factor
{1.0, 1.0};
1769 TrIndication(tr
, &line
, &col
);
1770 TrReadOperator(tr
, "[");
1772 if(TrIsOperator(tr
, "*"))
1774 TrReadOperator(tr
, "*");
1775 if(!TrReadOperator(tr
, "]") || !TrReadOperator(tr
, "="))
1778 TrIndication(tr
, &line
, &col
);
1780 if(!ReadSofaRef(tr
, &src
))
1783 if(hData
->mChannelType
== CT_STEREO
)
1785 const auto type
= TrReadIdent(tr
);
1789 switch(MatchChannelType(type
))
1792 TrErrorAt(tr
, line
, col
, "Expected a channel type.\n");
1804 const auto type
= TrReadIdent(tr
);
1808 if(MatchChannelType(type
) != CT_MONO
)
1810 TrErrorAt(tr
, line
, col
, "Expected a mono channel type.\n");
1816 MYSOFA_EASY
*sofa
{LoadSofaFile(&src
, hData
->mIrRate
, hData
->mIrPoints
)};
1819 const auto srcPosValues
= al::span
{sofa
->hrtf
->SourcePosition
.values
,
1820 sofa
->hrtf
->M
*3_uz
};
1821 for(uint si
{0};si
< sofa
->hrtf
->M
;++si
)
1823 printf("\rLoading sources... %d of %d", si
+1, sofa
->hrtf
->M
);
1826 std::array aer
{srcPosValues
[3_uz
*si
], srcPosValues
[3_uz
*si
+ 1],
1827 srcPosValues
[3_uz
*si
+ 2]};
1828 mysofa_c2s(aer
.data());
1830 if(std::fabs(aer
[1]) >= 89.999f
)
1833 aer
[0] = std::fmod(360.0f
- aer
[0], 360.0f
);
1835 auto field
= std::find_if(hData
->mFds
.cbegin(), hData
->mFds
.cend(),
1836 [&aer
](const HrirFdT
&fld
) -> bool
1837 { return (std::abs(aer
[2] - fld
.mDistance
) < 0.001); });
1838 if(field
== hData
->mFds
.cend())
1840 fi
= static_cast<uint
>(std::distance(hData
->mFds
.cbegin(), field
));
1842 const double evscale
{180.0 / static_cast<double>(field
->mEvs
.size()-1)};
1843 double ef
{(90.0 + aer
[1]) / evscale
};
1844 ei
= static_cast<uint
>(std::round(ef
));
1845 ef
= (ef
- ei
) * evscale
;
1846 if(std::abs(ef
) >= 0.1)
1849 const double azscale
{360.0 / static_cast<double>(field
->mEvs
[ei
].mAzs
.size())};
1850 double af
{aer
[0] / azscale
};
1851 ai
= static_cast<uint
>(std::round(af
));
1852 af
= (af
- ai
) * azscale
;
1853 ai
%= static_cast<uint
>(field
->mEvs
[ei
].mAzs
.size());
1854 if(std::abs(af
) >= 0.1)
1857 HrirAzT
*azd
= &field
->mEvs
[ei
].mAzs
[ai
];
1858 if(!azd
->mIrs
[0].empty())
1860 TrErrorAt(tr
, line
, col
, "Redefinition of source [ %d, %d, %d ].\n", fi
, ei
, ai
);
1864 const auto hrirPoints
= al::span
{hrir
}.first(hData
->mIrPoints
);
1865 ExtractSofaHrir(sofa
->hrtf
, si
, 0, src
.mOffset
, hrirPoints
);
1866 azd
->mIrs
[0] = hrirs
.subspan(size_t{hData
->mIrSize
}*azd
->mIndex
, hData
->mIrSize
);
1867 azd
->mDelays
[0] = AverageHrirOnset(onsetResampler
, onsetSamples
, hData
->mIrRate
,
1868 hrirPoints
, 1.0, azd
->mDelays
[0]);
1870 resampler
->process(hrirPoints
, hrir
);
1871 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0,
1874 if(src
.mChannel
== 1)
1876 ExtractSofaHrir(sofa
->hrtf
, si
, 1, src
.mOffset
, hrirPoints
);
1877 azd
->mIrs
[1] = hrirs
.subspan(
1878 (size_t{hData
->mIrCount
}+azd
->mIndex
) * hData
->mIrSize
, hData
->mIrSize
);
1879 azd
->mDelays
[1] = AverageHrirOnset(onsetResampler
, onsetSamples
,
1880 hData
->mIrRate
, hrirPoints
, 1.0, azd
->mDelays
[1]);
1882 resampler
->process(hrirPoints
, hrir
);
1883 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0,
1887 // TODO: Since some SOFA files contain minimum phase HRIRs,
1888 // it would be beneficial to check for per-measurement delays
1889 // (when available) to reconstruct the HRTDs.
1895 if(!ReadIndexTriplet(tr
, hData
, &fi
, &ei
, &ai
))
1897 if(!TrReadOperator(tr
, "]"))
1899 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
1901 if(!azd
->mIrs
[0].empty())
1903 TrErrorAt(tr
, line
, col
, "Redefinition of source.\n");
1906 if(!TrReadOperator(tr
, "="))
1912 if(!ReadSourceRef(tr
, &src
))
1915 // TODO: Would be nice to display 'x of y files', but that would
1916 // require preparing the source refs first to get a total count
1917 // before loading them.
1919 printf("\rLoading sources... %d file%s", count
, (count
==1)?"":"s");
1922 if(!LoadSource(&src
, hData
->mIrRate
, al::span
{hrir
}.first(hData
->mIrPoints
)))
1926 if(hData
->mChannelType
== CT_STEREO
)
1928 const auto ident
= TrReadIdent(tr
);
1932 if(auto earopt
= MatchTargetEar(ident
))
1936 TrErrorAt(tr
, line
, col
, "Expected a target ear.\n");
1940 const auto hrirPoints
= al::span
{hrir
}.first(hData
->mIrPoints
);
1941 azd
->mIrs
[ti
] = hrirs
.subspan((ti
*size_t{hData
->mIrCount
}+azd
->mIndex
)*hData
->mIrSize
,
1943 azd
->mDelays
[ti
] = AverageHrirOnset(onsetResampler
, onsetSamples
, hData
->mIrRate
,
1944 hrirPoints
, 1.0/factor
[ti
], azd
->mDelays
[ti
]);
1946 resampler
->process(hrirPoints
, hrir
);
1947 AverageHrirMagnitude(hData
->mFftSize
, al::span
{hrir
}.first(irPoints
), 1.0/factor
[ti
],
1950 if(!TrIsOperator(tr
, "+"))
1952 TrReadOperator(tr
, "+");
1954 if(hData
->mChannelType
== CT_STEREO
)
1956 if(azd
->mIrs
[0].empty())
1958 TrErrorAt(tr
, line
, col
, "Missing left ear source reference(s).\n");
1961 if(azd
->mIrs
[1].empty())
1963 TrErrorAt(tr
, line
, col
, "Missing right ear source reference(s).\n");
1972 hData
->mIrRate
= outRate
;
1973 hData
->mIrPoints
= irPoints
;
1976 for(fi
= 0;fi
< hData
->mFds
.size();fi
++)
1978 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
1980 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
1982 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
1983 if(!azd
->mIrs
[0].empty())
1986 if(ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size())
1989 if(ei
>= hData
->mFds
[fi
].mEvs
.size())
1991 TrError(tr
, "Missing source references [ %d, *, * ].\n", fi
);
1994 hData
->mFds
[fi
].mEvStart
= ei
;
1995 for(;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
1997 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
1999 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
2001 if(azd
->mIrs
[0].empty())
2003 TrError(tr
, "Missing source reference [ %d, %d, %d ].\n", fi
, ei
, ai
);
2009 for(uint ti
{0};ti
< channels
;ti
++)
2011 for(fi
= 0;fi
< hData
->mFds
.size();fi
++)
2013 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvs
.size();ei
++)
2015 for(ai
= 0;ai
< hData
->mFds
[fi
].mEvs
[ei
].mAzs
.size();ai
++)
2017 HrirAzT
*azd
= &hData
->mFds
[fi
].mEvs
[ei
].mAzs
[ai
];
2018 azd
->mIrs
[ti
] = hrirs
.subspan(
2019 (ti
*size_t{hData
->mIrCount
} + azd
->mIndex
) * hData
->mIrSize
,
2031 TrError(tr
, "Errant data at end of source list.\n");
2038 bool LoadDefInput(std::istream
&istream
, const al::span
<const char> startbytes
,
2039 const std::string_view filename
, const uint fftSize
, const uint truncSize
, const uint outRate
,
2040 const ChannelModeT chanMode
, HrirDataT
*hData
)
2042 TokenReaderT tr
{istream
};
2044 TrSetup(startbytes
, filename
, &tr
);
2045 if(!ProcessMetrics(&tr
, fftSize
, truncSize
, chanMode
, hData
)
2046 || !ProcessSources(&tr
, hData
, outRate
))