2 * HRTF utility for producing and demonstrating the process of creating an
3 * OpenAL Soft compatible HRIR data set.
5 * Copyright (C) 2011-2012 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
23 * --------------------------------------------------------------------------
25 * A big thanks goes out to all those whose work done in the field of
26 * binaural sound synthesis using measured HRTFs makes this utility and the
27 * OpenAL Soft implementation possible.
29 * The algorithm for diffuse-field equalization was adapted from the work
30 * done by Rio Emmanuel and Larcher Veronique of IRCAM and Bill Gardner of
31 * MIT Media Laboratory. It operates as follows:
33 * 1. Take the FFT of each HRIR and only keep the magnitude responses.
34 * 2. Calculate the diffuse-field power-average of all HRIRs weighted by
35 * their contribution to the total surface area covered by their
37 * 3. Take the diffuse-field average and limit its magnitude range.
38 * 4. Equalize the responses by using the inverse of the diffuse-field
40 * 5. Reconstruct the minimum-phase responses.
41 * 5. Zero the DC component.
42 * 6. IFFT the result and truncate to the desired-length minimum-phase FIR.
44 * The spherical head algorithm for calculating propagation delay was adapted
47 * Modeling Interaural Time Difference Assuming a Spherical Head
49 * Music 150, Musical Acoustics, Stanford University
54 /* Needed for 64-bit unsigned integer. */
64 // Rely (if naively) on OpenAL's header for the types used for serialization.
68 #define M_PI 3.14159265358979323846
72 #define HUGE_VAL (1.0/0.0)
75 // The epsilon used to maintain signal stability.
76 #define EPSILON (1e-15)
78 // Constants for accessing the token reader's ring buffer.
79 #define TR_RING_BITS (16)
80 #define TR_RING_SIZE (1 << TR_RING_BITS)
81 #define TR_RING_MASK (TR_RING_SIZE - 1)
83 // The token reader's load interval in bytes.
84 #define TR_LOAD_SIZE (TR_RING_SIZE >> 2)
86 // The maximum identifier length used when processing the data set
88 #define MAX_IDENT_LEN (16)
90 // The maximum path length used when processing filenames.
91 #define MAX_PATH_LEN (256)
93 // The limits for the sample 'rate' metric in the data set definition.
94 #define MIN_RATE (32000)
95 #define MAX_RATE (96000)
97 // The limits for the HRIR 'points' metric in the data set definition.
98 #define MIN_POINTS (16)
99 #define MAX_POINTS (8192)
101 // The limits to the number of 'azimuths' listed in the data set definition.
102 #define MIN_EV_COUNT (5)
103 #define MAX_EV_COUNT (128)
105 // The limits for each of the 'azimuths' listed in the data set definition.
106 #define MIN_AZ_COUNT (1)
107 #define MAX_AZ_COUNT (128)
109 // The limits for the listener's head 'radius' in the data set definition.
110 #define MIN_RADIUS (0.05)
111 #define MAX_RADIUS (0.15)
113 // The limits for the 'distance' from source to listener in the definition
115 #define MIN_DISTANCE (0.5)
116 #define MAX_DISTANCE (2.5)
118 // The maximum number of channels that can be addressed for a WAVE file
119 // source listed in the data set definition.
120 #define MAX_WAVE_CHANNELS (65535)
122 // The limits to the byte size for a binary source listed in the definition
124 #define MIN_BIN_SIZE (2)
125 #define MAX_BIN_SIZE (4)
127 // The minimum number of significant bits for binary sources listed in the
128 // data set definition. The maximum is calculated from the byte size.
129 #define MIN_BIN_BITS (16)
131 // The limits to the number of significant bits for an ASCII source listed in
132 // the data set definition.
133 #define MIN_ASCII_BITS (16)
134 #define MAX_ASCII_BITS (32)
136 // The limits to the FFT window size override on the command line.
137 #define MIN_FFTSIZE (512)
138 #define MAX_FFTSIZE (16384)
140 // The limits to the equalization range limit on the command line.
141 #define MIN_LIMIT (2.0)
142 #define MAX_LIMIT (120.0)
144 // The limits to the truncation window size on the command line.
145 #define MIN_TRUNCSIZE (8)
146 #define MAX_TRUNCSIZE (128)
148 // The truncation window size must be a multiple of the below value to allow
149 // for vectorized convolution.
150 #define MOD_TRUNCSIZE (8)
152 // The defaults for the command line options.
153 #define DEFAULT_EQUALIZE (1)
154 #define DEFAULT_SURFACE (1)
155 #define DEFAULT_LIMIT (24.0)
156 #define DEFAULT_TRUNCSIZE (32)
158 // The four-character-codes for RIFF/RIFX WAVE file chunks.
159 #define FOURCC_RIFF (0x46464952) // 'RIFF'
160 #define FOURCC_RIFX (0x58464952) // 'RIFX'
161 #define FOURCC_WAVE (0x45564157) // 'WAVE'
162 #define FOURCC_FMT (0x20746D66) // 'fmt '
163 #define FOURCC_DATA (0x61746164) // 'data'
164 #define FOURCC_LIST (0x5453494C) // 'LIST'
165 #define FOURCC_WAVL (0x6C766177) // 'wavl'
166 #define FOURCC_SLNT (0x746E6C73) // 'slnt'
168 // The supported wave formats.
169 #define WAVE_FORMAT_PCM (0x0001)
170 #define WAVE_FORMAT_IEEE_FLOAT (0x0003)
171 #define WAVE_FORMAT_EXTENSIBLE (0xFFFE)
173 // The maximum propagation delay value supported by OpenAL Soft.
174 #define MAX_HRTD (63.0)
176 // The OpenAL Soft HRTF format marker. It stands for minimum-phase head
177 // response protocol 01.
178 #define MHR_FORMAT ("MinPHR01")
180 // Byte order for the serialization routines.
187 // Source format for the references listed in the data set definition.
190 SF_WAVE
, // RIFF/RIFX WAVE file.
191 SF_BIN_LE
, // Little-endian binary file.
192 SF_BIN_BE
, // Big-endian binary file.
193 SF_ASCII
// ASCII text file.
196 // Element types for the references listed in the data set definition.
199 ET_INT
, // Integer elements.
200 ET_FP
// Floating-point elements.
203 // Desired output format from the command line.
206 OF_MHR
, // OpenAL Soft MHR data set file.
207 OF_TABLE
// OpenAL Soft built-in table file (used when compiling).
210 // Unsigned integer type.
211 typedef unsigned int uint
;
213 // Serialization types. The trailing digit indicates the number of bytes.
214 typedef ALubyte uint1
;
217 typedef ALuint uint4
;
219 #if defined (HAVE_STDINT_H)
222 typedef uint64_t uint8
;
223 #elif defined (HAVE___INT64)
224 typedef unsigned __int64 uint8
;
225 #elif (SIZEOF_LONG == 8)
226 typedef unsigned long uint8
;
227 #elif (SIZEOF_LONG_LONG == 8)
228 typedef unsigned long long uint8
;
231 typedef enum ByteOrderT ByteOrderT
;
232 typedef enum SourceFormatT SourceFormatT
;
233 typedef enum ElementTypeT ElementTypeT
;
234 typedef enum OutputFormatT OutputFormatT
;
236 typedef struct TokenReaderT TokenReaderT
;
237 typedef struct SourceRefT SourceRefT
;
238 typedef struct HrirDataT HrirDataT
;
240 // Token reader state for parsing the data set definition.
241 struct TokenReaderT
{
246 char mRing
[TR_RING_SIZE
];
251 // Source reference state used when loading sources.
253 SourceFormatT mFormat
;
260 char mPath
[MAX_PATH_LEN
+ 1];
263 // The HRIR metrics and data set used when loading, processing, and storing
264 // the resulting HRTF.
273 mAzCount
[MAX_EV_COUNT
],
274 mEvOffset
[MAX_EV_COUNT
];
282 /* Token reader routines for parsing text files. Whitespace is not
283 * significant. It can process tokens as identifiers, numbers (integer and
284 * floating-point), strings, and operators. Strings must be encapsulated by
285 * double-quotes and cannot span multiple lines.
288 // Setup the reader on the given file. The filename can be NULL if no error
289 // output is desired.
290 static void TrSetup (FILE * fp
, const char * filename
, TokenReaderT
* tr
) {
291 const char * name
= NULL
;
296 // If a filename was given, store a pointer to the base name.
297 if (filename
!= NULL
) {
298 while ((ch
= (* filename
)) != '\0') {
299 if ((ch
== '/') || (ch
== '\\'))
311 // Prime the reader's ring buffer, and return a result indicating that there
312 // is text to process.
313 static int TrLoad (TokenReaderT
* tr
) {
314 size_t toLoad
, in
, count
;
316 toLoad
= TR_RING_SIZE
- (tr
-> mIn
- tr
-> mOut
);
317 if ((toLoad
>= TR_LOAD_SIZE
) && (! feof (tr
-> mFile
))) {
318 // Load TR_LOAD_SIZE (or less if at the end of the file) per read.
319 toLoad
= TR_LOAD_SIZE
;
320 in
= tr
-> mIn
& TR_RING_MASK
;
321 count
= TR_RING_SIZE
- in
;
322 if (count
< toLoad
) {
323 tr
-> mIn
+= fread (& tr
-> mRing
[in
], 1, count
, tr
-> mFile
);
324 tr
-> mIn
+= fread (& tr
-> mRing
[0], 1, toLoad
- count
, tr
-> mFile
);
326 tr
-> mIn
+= fread (& tr
-> mRing
[in
], 1, toLoad
, tr
-> mFile
);
328 if (tr
-> mOut
>= TR_RING_SIZE
) {
329 tr
-> mOut
-= TR_RING_SIZE
;
330 tr
-> mIn
-= TR_RING_SIZE
;
333 if (tr
-> mIn
> tr
-> mOut
)
338 // Error display routine. Only displays when the base name is not NULL.
339 static void TrErrorVA (const TokenReaderT
* tr
, uint line
, uint column
, const char * format
, va_list argPtr
) {
340 if (tr
-> mName
!= NULL
) {
341 fprintf (stderr
, "Error (%s:%d:%d): ", tr
-> mName
, line
, column
);
342 vfprintf (stderr
, format
, argPtr
);
346 // Used to display an error at a saved line/column.
347 static void TrErrorAt (const TokenReaderT
* tr
, uint line
, uint column
, const char * format
, ...) {
350 va_start (argPtr
, format
);
351 TrErrorVA (tr
, line
, column
, format
, argPtr
);
355 // Used to display an error at the current line/column.
356 static void TrError (const TokenReaderT
* tr
, const char * format
, ...) {
359 va_start (argPtr
, format
);
360 TrErrorVA (tr
, tr
-> mLine
, tr
-> mColumn
, format
, argPtr
);
364 // Skips to the next line.
365 static void TrSkipLine (TokenReaderT
* tr
) {
368 while (TrLoad (tr
)) {
369 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
380 // Skips to the next token.
381 static int TrSkipWhitespace (TokenReaderT
* tr
) {
384 while (TrLoad (tr
)) {
385 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
394 } else if (ch
== '#') {
403 // Get the line and/or column of the next token (or the end of input).
404 static void TrIndication (TokenReaderT
* tr
, uint
* line
, uint
* column
) {
405 TrSkipWhitespace (tr
);
407 (* line
) = tr
-> mLine
;
409 (* column
) = tr
-> mColumn
;
412 // Checks to see if a token is the given operator. It does not display any
413 // errors and will not proceed to the next token.
414 static int TrIsOperator (TokenReaderT
* tr
, const char * op
) {
418 if (! TrSkipWhitespace (tr
))
422 while ((op
[len
] != '\0') && (out
< tr
-> mIn
)) {
423 ch
= tr
-> mRing
[out
& TR_RING_MASK
];
429 if (op
[len
] == '\0')
434 /* The TrRead*() routines obtain the value of a matching token type. They
435 * display type, form, and boundary errors and will proceed to the next
439 // Reads and validates an identifier token.
440 static int TrReadIdent (TokenReaderT
* tr
, const size_t maxLen
, char * ident
) {
446 if (TrSkipWhitespace (tr
)) {
448 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
449 if ((ch
== '_') || isalpha (ch
)) {
458 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
459 } while ((ch
== '_') || isdigit (ch
) || isalpha (ch
));
460 tr
-> mColumn
+= len
;
462 TrErrorAt (tr
, tr
-> mLine
, col
, "Identifier is too long.\n");
469 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected an identifier.\n");
473 // Reads and validates (including bounds) an integer token.
474 static int TrReadInt (TokenReaderT
* tr
, const int loBound
, const int hiBound
, int * value
) {
477 char ch
, temp
[64 + 1];
480 if (TrSkipWhitespace (tr
)) {
483 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
484 if ((ch
== '+') || (ch
== '-')) {
490 while (TrLoad (tr
)) {
491 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
500 tr
-> mColumn
+= len
;
501 if ((digis
> 0) && (ch
!= '.') && (! isalpha (ch
))) {
503 TrErrorAt (tr
, tr
-> mLine
, col
, "Integer is too long.");
507 (* value
) = strtol (temp
, NULL
, 10);
508 if (((* value
) < loBound
) || ((* value
) > hiBound
)) {
509 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected a value from %d to %d.\n", loBound
, hiBound
);
515 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected an integer.\n");
519 // Reads and validates (including bounds) a float token.
520 static int TrReadFloat (TokenReaderT
* tr
, const double loBound
, const double hiBound
, double * value
) {
523 char ch
, temp
[64 + 1];
526 if (TrSkipWhitespace (tr
)) {
529 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
530 if ((ch
== '+') || (ch
== '-')) {
536 while (TrLoad (tr
)) {
537 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
552 while (TrLoad (tr
)) {
553 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
563 if ((ch
== 'E') || (ch
== 'e')) {
569 if ((ch
== '+') || (ch
== '-')) {
575 while (TrLoad (tr
)) {
576 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
586 tr
-> mColumn
+= len
;
587 if ((digis
> 0) && (ch
!= '.') && (! isalpha (ch
))) {
589 TrErrorAt (tr
, tr
-> mLine
, col
, "Float is too long.");
593 (* value
) = strtod (temp
, NULL
);
594 if (((* value
) < loBound
) || ((* value
) > hiBound
)) {
595 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected a value from %f to %f.\n", loBound
, hiBound
);
601 tr
-> mColumn
+= len
;
604 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected a float.\n");
608 // Reads and validates a string token.
609 static int TrReadString (TokenReaderT
* tr
, const size_t maxLen
, char * text
) {
615 if (TrSkipWhitespace (tr
)) {
617 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
621 while (TrLoad (tr
)) {
622 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
627 TrErrorAt (tr
, tr
-> mLine
, col
, "Unterminated string at end of line.\n");
635 tr
-> mColumn
+= 1 + len
;
636 TrErrorAt (tr
, tr
-> mLine
, col
, "Unterminated string at end of input.\n");
639 tr
-> mColumn
+= 2 + len
;
641 TrErrorAt (tr
, tr
-> mLine
, col
, "String is too long.\n");
648 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected a string.\n");
652 // Reads and validates the given operator.
653 static int TrReadOperator (TokenReaderT
* tr
, const char * op
) {
659 if (TrSkipWhitespace (tr
)) {
662 while ((op
[len
] != '\0') && TrLoad (tr
)) {
663 ch
= tr
-> mRing
[tr
-> mOut
& TR_RING_MASK
];
669 tr
-> mColumn
+= len
;
670 if (op
[len
] == '\0')
673 TrErrorAt (tr
, tr
-> mLine
, col
, "Expected '%s' operator.\n", op
);
677 /* Performs a string substitution. Any case-insensitive occurrences of the
678 * pattern string are replaced with the replacement string. The result is
679 * truncated if necessary.
681 static int StrSubst (const char * in
, const char * pat
, const char * rep
, const size_t maxLen
, char * out
) {
682 size_t inLen
, patLen
, repLen
;
687 patLen
= strlen (pat
);
688 repLen
= strlen (rep
);
692 while ((si
< inLen
) && (di
< maxLen
)) {
693 if (patLen
<= (inLen
- si
)) {
694 if (strncasecmp (& in
[si
], pat
, patLen
) == 0) {
695 if (repLen
> (maxLen
- di
)) {
696 repLen
= maxLen
- di
;
699 strncpy (& out
[di
], rep
, repLen
);
711 return (! truncated
);
714 // Provide missing math routines for MSVC.
716 static double round (double val
) {
718 return (ceil (val
- 0.5));
719 return (floor (val
+ 0.5));
722 static double fmin (double a
, double b
) {
723 return ((a
< b
) ? a
: b
);
726 static double fmax (double a
, double b
) {
727 return ((a
> b
) ? a
: b
);
731 // Simple clamp routine.
732 static double Clamp (const double val
, const double lower
, const double upper
) {
733 return (fmin (fmax (val
, lower
), upper
));
736 // Performs linear interpolation.
737 static double Lerp (const double a
, const double b
, const double f
) {
738 return (a
+ (f
* (b
- a
)));
741 // Performs a high-passed triangular probability density function dither from
742 // a double to an integer. It assumes the input sample is already scaled.
743 static int HpTpdfDither (const double in
, int * hpHist
) {
744 const double PRNG_SCALE
= 1.0 / RAND_MAX
;
749 out
= round (in
+ (PRNG_SCALE
* (prn
- (* hpHist
))));
754 // Allocates an array of doubles.
755 static double * CreateArray (const size_t n
) {
758 a
= (double *) calloc (n
, sizeof (double));
760 fprintf (stderr
, "Error: Out of memory.\n");
766 // Frees an array of doubles.
767 static void DestroyArray (const double * a
) {
771 // Complex number routines. All outputs must be non-NULL.
773 // Magnitude/absolute value.
774 static double ComplexAbs (const double r
, const double i
) {
775 return (sqrt ((r
* r
) + (i
* i
)));
779 static void ComplexMul (const double aR
, const double aI
, const double bR
, const double bI
, double * outR
, double * outI
) {
780 (* outR
) = (aR
* bR
) - (aI
* bI
);
781 (* outI
) = (aI
* bR
) + (aR
* bI
);
785 static void ComplexExp (const double inR
, const double inI
, double * outR
, double * outI
) {
789 (* outR
) = e
* cos (inI
);
790 (* outI
) = e
* sin (inI
);
793 /* Fast Fourier transform routines. The number of points must be a power of
794 * two. In-place operation is possible only if both the real and imaginary
795 * parts are in-place together.
798 // Performs bit-reversal ordering.
799 static void FftArrange (const size_t n
, const double * inR
, const double * inI
, double * outR
, double * outI
) {
803 if ((inR
== outR
) && (inI
== outI
)) {
804 // Handle in-place arrangement.
806 for (k
= 0; k
< n
; k
++) {
816 while (rk
& (m
>>= 1))
821 // Handle copy arrangement.
823 for (k
= 0; k
< n
; k
++) {
827 while (rk
& (m
>>= 1))
834 // Performs the summation.
835 static void FftSummation (const size_t n
, const double s
, double * re
, double * im
) {
838 double vR
, vI
, wR
, wI
;
843 for (m
= 1, m2
= 2; m
< n
; m
<<= 1, m2
<<= 1) {
844 // v = Complex (-2.0 * sin (0.5 * pi / m) * sin (0.5 * pi / m), -sin (pi / m))
845 vR
= sin (0.5 * pi
/ m
);
848 // w = Complex (1.0, 0.0)
851 for (i
= 0; i
< m
; i
++) {
852 for (k
= i
; k
< n
; k
+= m2
) {
854 // t = ComplexMul (w, out [km2])
855 tR
= (wR
* re
[mk
]) - (wI
* im
[mk
]);
856 tI
= (wR
* im
[mk
]) + (wI
* re
[mk
]);
857 // out [mk] = ComplexSub (out [k], t)
858 re
[mk
] = re
[k
] - tR
;
859 im
[mk
] = im
[k
] - tI
;
860 // out [k] = ComplexAdd (out [k], t)
864 // t = ComplexMul (v, w)
865 tR
= (vR
* wR
) - (vI
* wI
);
866 tI
= (vR
* wI
) + (vI
* wR
);
867 // w = ComplexAdd (w, t)
874 // Performs a forward FFT.
875 static void FftForward (const size_t n
, const double * inR
, const double * inI
, double * outR
, double * outI
) {
876 FftArrange (n
, inR
, inI
, outR
, outI
);
877 FftSummation (n
, 1.0, outR
, outI
);
880 // Performs an inverse FFT.
881 static void FftInverse (const size_t n
, const double * inR
, const double * inI
, double * outR
, double * outI
) {
885 FftArrange (n
, inR
, inI
, outR
, outI
);
886 FftSummation (n
, -1.0, outR
, outI
);
888 for (i
= 0; i
< n
; i
++) {
894 /* Calculate the complex helical sequence (or discrete-time analytical
895 * signal) of the given input using the Hilbert transform. Given the
896 * negative natural logarithm of a signal's magnitude response, the imaginary
897 * components can be used as the angles for minimum-phase reconstruction.
899 static void Hilbert (const size_t n
, const double * in
, double * outR
, double * outI
) {
903 // Handle in-place operation.
904 for (i
= 0; i
< n
; i
++)
907 // Handle copy operation.
908 for (i
= 0; i
< n
; i
++) {
913 FftForward (n
, outR
, outI
, outR
, outI
);
914 /* Currently the Fourier routines operate only on point counts that are
915 * powers of two. If that changes and n is odd, the following conditional
916 * should be: i < (n + 1) / 2.
918 for (i
= 1; i
< (n
/ 2); i
++) {
922 // If n is odd, the following increment should be skipped.
924 for (; i
< n
; i
++) {
928 FftInverse (n
, outR
, outI
, outR
, outI
);
931 /* Calculate the magnitude response of the given input. This is used in
932 * place of phase decomposition, since the phase residuals are discarded for
933 * minimum phase reconstruction. The mirrored half of the response is also
936 static void MagnitudeResponse (const size_t n
, const double * inR
, const double * inI
, double * out
) {
937 const size_t m
= 1 + (n
/ 2);
940 for (i
= 0; i
< m
; i
++)
941 out
[i
] = fmax (ComplexAbs (inR
[i
], inI
[i
]), EPSILON
);
944 /* Apply a range limit (in dB) to the given magnitude response. This is used
945 * to adjust the effects of the diffuse-field average on the equalization
948 static void LimitMagnitudeResponse (const size_t n
, const double limit
, const double * in
, double * out
) {
949 const size_t m
= 1 + (n
/ 2);
951 size_t i
, lower
, upper
;
954 halfLim
= limit
/ 2.0;
955 // Convert the response to dB.
956 for (i
= 0; i
< m
; i
++)
957 out
[i
] = 20.0 * log10 (in
[i
]);
958 // Use six octaves to calculate the average magnitude of the signal.
959 lower
= ((size_t) ceil (n
/ pow (2.0, 8.0))) - 1;
960 upper
= ((size_t) floor (n
/ pow (2.0, 2.0))) - 1;
962 for (i
= lower
; i
<= upper
; i
++)
964 ave
/= upper
- lower
+ 1;
965 // Keep the response within range of the average magnitude.
966 for (i
= 0; i
< m
; i
++)
967 out
[i
] = Clamp (out
[i
], ave
- halfLim
, ave
+ halfLim
);
968 // Convert the response back to linear magnitude.
969 for (i
= 0; i
< m
; i
++)
970 out
[i
] = pow (10.0, out
[i
] / 20.0);
973 /* Reconstructs the minimum-phase component for the given magnitude response
974 * of a signal. This is equivalent to phase recomposition, sans the missing
975 * residuals (which were discarded). The mirrored half of the response is
978 static void MinimumPhase (const size_t n
, const double * in
, double * outR
, double * outI
) {
979 const size_t m
= 1 + (n
/ 2);
980 double * mags
= NULL
;
984 mags
= CreateArray (n
);
985 for (i
= 0; i
< m
; i
++) {
986 mags
[i
] = fmax (in
[i
], EPSILON
);
987 outR
[i
] = -log (mags
[i
]);
989 for (; i
< n
; i
++) {
990 mags
[i
] = mags
[n
- i
];
991 outR
[i
] = outR
[n
- i
];
993 Hilbert (n
, outR
, outR
, outI
);
994 // Remove any DC offset the filter has.
997 for (i
= 1; i
< n
; i
++) {
998 ComplexExp (0.0, outI
[i
], & aR
, & aI
);
999 ComplexMul (mags
[i
], 0.0, aR
, aI
, & outR
[i
], & outI
[i
]);
1001 DestroyArray (mags
);
1004 // Read a binary value of the specified byte order and byte size from a file,
1005 // storing it as a 32-bit unsigned integer.
1006 static int ReadBin4 (FILE * fp
, const char * filename
, const ByteOrderT order
, const uint bytes
, uint4
* out
) {
1011 if (fread (in
, 1, bytes
, fp
) != bytes
) {
1012 fprintf (stderr
, "Error: Bad read from file '%s'.\n", filename
);
1018 for (i
= 0; i
< bytes
; i
++)
1019 accum
= (accum
<< 8) | in
[bytes
- i
- 1];
1022 for (i
= 0; i
< bytes
; i
++)
1023 accum
= (accum
<< 8) | in
[i
];
1032 // Read a binary value of the specified byte order from a file, storing it as
1033 // a 64-bit unsigned integer.
1034 static int ReadBin8 (FILE * fp
, const char * filename
, const ByteOrderT order
, uint8
* out
) {
1039 if (fread (in
, 1, 8, fp
) != 8) {
1040 fprintf (stderr
, "Error: Bad read from file '%s'.\n", filename
);
1046 for (i
= 0; i
< 8; i
++)
1047 accum
= (accum
<< 8) | in
[8 - i
- 1];
1050 for (i
= 0; i
< 8; i
++)
1051 accum
= (accum
<< 8) | in
[i
];
1060 // Write an ASCII string to a file.
1061 static int WriteAscii (const char * out
, FILE * fp
, const char * filename
) {
1065 if (fwrite (out
, 1, len
, fp
) != len
) {
1067 fprintf (stderr
, "Error: Bad write to file '%s'.\n", filename
);
1073 // Write a binary value of the given byte order and byte size to a file,
1074 // loading it from a 32-bit unsigned integer.
1075 static int WriteBin4 (const ByteOrderT order
, const uint bytes
, const uint4 in
, FILE * fp
, const char * filename
) {
1081 for (i
= 0; i
< bytes
; i
++)
1082 out
[i
] = (in
>> (i
* 8)) & 0x000000FF;
1085 for (i
= 0; i
< bytes
; i
++)
1086 out
[bytes
- i
- 1] = (in
>> (i
* 8)) & 0x000000FF;
1091 if (fwrite (out
, 1, bytes
, fp
) != bytes
) {
1092 fprintf (stderr
, "Error: Bad write to file '%s'.\n", filename
);
1098 /* Read a binary value of the specified type, byte order, and byte size from
1099 * a file, converting it to a double. For integer types, the significant
1100 * bits are used to normalize the result. The sign of bits determines
1101 * whether they are padded toward the MSB (negative) or LSB (positive).
1102 * Floating-point types are not normalized.
1104 static int ReadBinAsDouble (FILE * fp
, const char * filename
, const ByteOrderT order
, const ElementTypeT type
, const uint bytes
, const int bits
, double * out
) {
1117 if (! ReadBin8 (fp
, filename
, order
, & v8
. ui
))
1122 if (! ReadBin4 (fp
, filename
, order
, bytes
, & v4
. ui
))
1124 if (type
== ET_FP
) {
1125 (* out
) = (double) v4
. f
;
1128 v4
. ui
>>= (8 * bytes
) - bits
;
1130 v4
. ui
&= (0xFFFFFFFF >> (32 + bits
));
1131 if (v4
. ui
& (1 << (abs (bits
) - 1)))
1132 v4
. ui
|= (0xFFFFFFFF << abs (bits
));
1133 (* out
) = v4
. i
/ ((double) (1 << (abs (bits
) - 1)));
1139 /* Read an ascii value of the specified type from a file, converting it to a
1140 * double. For integer types, the significant bits are used to normalize the
1141 * result. The sign of the bits should always be positive. This also skips
1142 * up to one separator character before the element itself.
1144 static int ReadAsciiAsDouble (TokenReaderT
* tr
, const char * filename
, const ElementTypeT type
, const uint bits
, double * out
) {
1147 if (TrIsOperator (tr
, ","))
1148 TrReadOperator (tr
, ",");
1149 else if (TrIsOperator (tr
, ":"))
1150 TrReadOperator (tr
, ":");
1151 else if (TrIsOperator (tr
, ";"))
1152 TrReadOperator (tr
, ";");
1153 else if (TrIsOperator (tr
, "|"))
1154 TrReadOperator (tr
, "|");
1155 if (type
== ET_FP
) {
1156 if (! TrReadFloat (tr
, -HUGE_VAL
, HUGE_VAL
, out
)) {
1157 fprintf (stderr
, "Error: Bad read from file '%s'.\n", filename
);
1161 if (! TrReadInt (tr
, -(1 << (bits
- 1)), (1 << (bits
- 1)) - 1, & v
)) {
1162 fprintf (stderr
, "Error: Bad read from file '%s'.\n", filename
);
1165 (* out
) = v
/ ((double) ((1 << (bits
- 1)) - 1));
1170 // Read the RIFF/RIFX WAVE format chunk from a file, validating it against
1171 // the source parameters and data set metrics.
1172 static int ReadWaveFormat (FILE * fp
, const ByteOrderT order
, const uint hrirRate
, SourceRefT
* src
) {
1173 uint4 fourCC
, chunkSize
;
1174 uint4 format
, channels
, rate
, dummy
, block
, size
, bits
;
1179 fseek (fp
, chunkSize
, SEEK_CUR
);
1180 if ((! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & fourCC
)) ||
1181 (! ReadBin4 (fp
, src
-> mPath
, order
, 4, & chunkSize
)))
1183 } while (fourCC
!= FOURCC_FMT
);
1184 if ((! ReadBin4 (fp
, src
-> mPath
, order
, 2, & format
)) ||
1185 (! ReadBin4 (fp
, src
-> mPath
, order
, 2, & channels
)) ||
1186 (! ReadBin4 (fp
, src
-> mPath
, order
, 4, & rate
)) ||
1187 (! ReadBin4 (fp
, src
-> mPath
, order
, 4, & dummy
)) ||
1188 (! ReadBin4 (fp
, src
-> mPath
, order
, 2, & block
)))
1191 if (chunkSize
> 14) {
1192 if (! ReadBin4 (fp
, src
-> mPath
, order
, 2, & size
))
1200 if (format
== WAVE_FORMAT_EXTENSIBLE
) {
1201 fseek (fp
, 2, SEEK_CUR
);
1202 if (! ReadBin4 (fp
, src
-> mPath
, order
, 2, & bits
))
1206 fseek (fp
, 4, SEEK_CUR
);
1207 if (! ReadBin4 (fp
, src
-> mPath
, order
, 2, & format
))
1209 fseek (fp
, chunkSize
- 26, SEEK_CUR
);
1213 fseek (fp
, chunkSize
- 16, SEEK_CUR
);
1215 fseek (fp
, chunkSize
- 14, SEEK_CUR
);
1217 if ((format
!= WAVE_FORMAT_PCM
) && (format
!= WAVE_FORMAT_IEEE_FLOAT
)) {
1218 fprintf (stderr
, "Error: Unsupported WAVE format in file '%s'.\n", src
-> mPath
);
1221 if (src
-> mChannel
>= channels
) {
1222 fprintf (stderr
, "Error: Missing source channel in WAVE file '%s'.\n", src
-> mPath
);
1225 if (rate
!= hrirRate
) {
1226 fprintf (stderr
, "Error: Mismatched source sample rate in WAVE file '%s'.\n", src
-> mPath
);
1229 if (format
== WAVE_FORMAT_PCM
) {
1230 if ((size
< 2) || (size
> 4)) {
1231 fprintf (stderr
, "Error: Unsupported sample size in WAVE file '%s'.\n", src
-> mPath
);
1234 if ((bits
< 16) || (bits
> (8 * size
))) {
1235 fprintf (stderr
, "Error: Bad significant bits in WAVE file '%s'.\n", src
-> mPath
);
1238 src
-> mType
= ET_INT
;
1240 if ((size
!= 4) && (size
!= 8)) {
1241 fprintf (stderr
, "Error: Unsupported sample size in WAVE file '%s'.\n", src
-> mPath
);
1244 src
-> mType
= ET_FP
;
1246 src
-> mSize
= size
;
1247 src
-> mBits
= (int) bits
;
1248 src
-> mSkip
= channels
;
1252 // Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles.
1253 static int ReadWaveData (FILE * fp
, const SourceRefT
* src
, const ByteOrderT order
, const size_t n
, double * hrir
) {
1254 int pre
, post
, skip
;
1257 pre
= (int) (src
-> mSize
* src
-> mChannel
);
1258 post
= (int) (src
-> mSize
* (src
-> mSkip
- src
-> mChannel
- 1));
1260 for (i
= 0; i
< n
; i
++) {
1263 fseek (fp
, skip
, SEEK_CUR
);
1264 if (! ReadBinAsDouble (fp
, src
-> mPath
, order
, src
-> mType
, src
-> mSize
, src
-> mBits
, & hrir
[i
]))
1269 fseek (fp
, skip
, SEEK_CUR
);
1273 // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to
1275 static int ReadWaveList (FILE * fp
, const SourceRefT
* src
, const ByteOrderT order
, const size_t n
, double * hrir
) {
1276 uint4 fourCC
, chunkSize
, listSize
;
1277 size_t block
, skip
, count
, offset
, i
;
1281 if ((! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & fourCC
)) ||
1282 (! ReadBin4 (fp
, src
-> mPath
, order
, 4, & chunkSize
)))
1284 if (fourCC
== FOURCC_DATA
) {
1285 block
= src
-> mSize
* src
-> mSkip
;
1286 count
= chunkSize
/ block
;
1287 if (count
< (src
-> mOffset
+ n
)) {
1288 fprintf (stderr
, "Error: Bad read from file '%s'.\n", src
-> mPath
);
1291 fseek (fp
, src
-> mOffset
* block
, SEEK_CUR
);
1292 if (! ReadWaveData (fp
, src
, order
, n
, & hrir
[0]))
1295 } else if (fourCC
== FOURCC_LIST
) {
1296 if (! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & fourCC
))
1299 if (fourCC
== FOURCC_WAVL
)
1303 fseek (fp
, chunkSize
, SEEK_CUR
);
1305 listSize
= chunkSize
;
1306 block
= src
-> mSize
* src
-> mSkip
;
1307 skip
= src
-> mOffset
;
1310 while ((offset
< n
) && (listSize
> 8)) {
1311 if ((! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & fourCC
)) ||
1312 (! ReadBin4 (fp
, src
-> mPath
, order
, 4, & chunkSize
)))
1314 listSize
-= 8 + chunkSize
;
1315 if (fourCC
== FOURCC_DATA
) {
1316 count
= chunkSize
/ block
;
1318 fseek (fp
, skip
* block
, SEEK_CUR
);
1319 chunkSize
-= skip
* block
;
1322 if (count
> (n
- offset
))
1324 if (! ReadWaveData (fp
, src
, order
, count
, & hrir
[offset
]))
1326 chunkSize
-= count
* block
;
1328 lastSample
= hrir
[offset
- 1];
1333 } else if (fourCC
== FOURCC_SLNT
) {
1334 if (! ReadBin4 (fp
, src
-> mPath
, order
, 4, & count
))
1340 if (count
> (n
- offset
))
1342 for (i
= 0; i
< count
; i
++)
1343 hrir
[offset
+ i
] = lastSample
;
1351 fseek (fp
, chunkSize
, SEEK_CUR
);
1354 fprintf (stderr
, "Error: Bad read from file '%s'.\n", src
-> mPath
);
1360 // Load a source HRIR from a RIFF/RIFX WAVE file.
1361 static int LoadWaveSource (FILE * fp
, SourceRefT
* src
, const uint hrirRate
, const size_t n
, double * hrir
) {
1362 uint4 fourCC
, dummy
;
1365 if ((! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & fourCC
)) ||
1366 (! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & dummy
)))
1368 if (fourCC
== FOURCC_RIFF
) {
1370 } else if (fourCC
== FOURCC_RIFX
) {
1373 fprintf (stderr
, "Error: No RIFF/RIFX chunk in file '%s'.\n", src
-> mPath
);
1376 if (! ReadBin4 (fp
, src
-> mPath
, BO_LITTLE
, 4, & fourCC
))
1378 if (fourCC
!= FOURCC_WAVE
) {
1379 fprintf (stderr
, "Error: Not a RIFF/RIFX WAVE file '%s'.\n", src
-> mPath
);
1382 if (! ReadWaveFormat (fp
, order
, hrirRate
, src
))
1384 if (! ReadWaveList (fp
, src
, order
, n
, hrir
))
1389 // Load a source HRIR from a binary file.
1390 static int LoadBinarySource (FILE * fp
, const SourceRefT
* src
, const ByteOrderT order
, const size_t n
, double * hrir
) {
1393 fseek (fp
, src
-> mOffset
, SEEK_SET
);
1394 for (i
= 0; i
< n
; i
++) {
1395 if (! ReadBinAsDouble (fp
, src
-> mPath
, order
, src
-> mType
, src
-> mSize
, src
-> mBits
, & hrir
[i
]))
1397 if (src
-> mSkip
> 0)
1398 fseek (fp
, src
-> mSkip
, SEEK_CUR
);
1403 // Load a source HRIR from an ASCII text file containing a list of elements
1404 // separated by whitespace or common list operators (',', ';', ':', '|').
1405 static int LoadAsciiSource (FILE * fp
, const SourceRefT
* src
, const size_t n
, double * hrir
) {
1410 TrSetup (fp
, NULL
, & tr
);
1411 for (i
= 0; i
< src
-> mOffset
; i
++) {
1412 if (! ReadAsciiAsDouble (& tr
, src
-> mPath
, src
-> mType
, src
-> mBits
, & dummy
))
1415 for (i
= 0; i
< n
; i
++) {
1416 if (! ReadAsciiAsDouble (& tr
, src
-> mPath
, src
-> mType
, src
-> mBits
, & hrir
[i
]))
1418 for (j
= 0; j
< src
-> mSkip
; j
++) {
1419 if (! ReadAsciiAsDouble (& tr
, src
-> mPath
, src
-> mType
, src
-> mBits
, & dummy
))
1426 // Load a source HRIR from a supported file type.
1427 static int LoadSource (SourceRefT
* src
, const uint hrirRate
, const size_t n
, double * hrir
) {
1431 if (src
-> mFormat
== SF_ASCII
)
1432 fp
= fopen (src
-> mPath
, "r");
1434 fp
= fopen (src
-> mPath
, "rb");
1436 fprintf (stderr
, "Error: Could not open source file '%s'.\n", src
-> mPath
);
1439 if (src
-> mFormat
== SF_WAVE
)
1440 result
= LoadWaveSource (fp
, src
, hrirRate
, n
, hrir
);
1441 else if (src
-> mFormat
== SF_BIN_LE
)
1442 result
= LoadBinarySource (fp
, src
, BO_LITTLE
, n
, hrir
);
1443 else if (src
-> mFormat
== SF_BIN_BE
)
1444 result
= LoadBinarySource (fp
, src
, BO_BIG
, n
, hrir
);
1446 result
= LoadAsciiSource (fp
, src
, n
, hrir
);
1451 // Calculate the magnitude response of an HRIR and average it with any
1452 // existing responses for its elevation and azimuth.
1453 static void AverageHrirMagnitude (const double * hrir
, const double f
, const uint ei
, const uint ai
, const HrirDataT
* hData
) {
1454 double * re
= NULL
, * im
= NULL
;
1457 n
= hData
-> mFftSize
;
1458 re
= CreateArray (n
);
1459 im
= CreateArray (n
);
1460 for (i
= 0; i
< hData
-> mIrPoints
; i
++) {
1464 for (; i
< n
; i
++) {
1468 FftForward (n
, re
, im
, re
, im
);
1469 MagnitudeResponse (n
, re
, im
, re
);
1471 j
= (hData
-> mEvOffset
[ei
] + ai
) * hData
-> mIrSize
;
1472 for (i
= 0; i
< m
; i
++)
1473 hData
-> mHrirs
[j
+ i
] = Lerp (hData
-> mHrirs
[j
+ i
], re
[i
], f
);
1478 /* Calculate the contribution of each HRIR to the diffuse-field average based
1479 * on the area of its surface patch. All patches are centered at the HRIR
1480 * coordinates on the unit sphere and are measured by solid angle.
1482 static void CalculateDfWeights (const HrirDataT
* hData
, double * weights
) {
1484 double evs
, sum
, ev
, up_ev
, down_ev
, solidAngle
;
1486 evs
= 90.0 / (hData
-> mEvCount
- 1);
1488 for (ei
= hData
-> mEvStart
; ei
< hData
-> mEvCount
; ei
++) {
1489 // For each elevation, calculate the upper and lower limits of the
1491 ev
= -90.0 + (ei
* 2.0 * evs
);
1492 if (ei
< (hData
-> mEvCount
- 1))
1493 up_ev
= (ev
+ evs
) * M_PI
/ 180.0;
1497 down_ev
= (ev
- evs
) * M_PI
/ 180.0;
1499 down_ev
= -M_PI
/ 2.0;
1500 // Calculate the area of the patch band.
1501 solidAngle
= 2.0 * M_PI
* (sin (up_ev
) - sin (down_ev
));
1502 // Each weight is the area of one patch.
1503 weights
[ei
] = solidAngle
/ hData
-> mAzCount
[ei
];
1504 // Sum the total surface area covered by the HRIRs.
1507 // Normalize the weights given the total surface coverage.
1508 for (ei
= hData
-> mEvStart
; ei
< hData
-> mEvCount
; ei
++)
1509 weights
[ei
] /= sum
;
1512 /* Calculate the diffuse-field average from the given magnitude responses of
1513 * the HRIR set. Weighting can be applied to compensate for the varying
1514 * surface area covered by each HRIR. The final average can then be limited
1515 * by the specified magnitude range (in positive dB; 0.0 to skip).
1517 static void CalculateDiffuseFieldAverage (const HrirDataT
* hData
, const int weighted
, const double limit
, double * dfa
) {
1518 double * weights
= NULL
;
1520 size_t count
, step
, start
, end
, m
, j
, i
;
1523 weights
= CreateArray (hData
-> mEvCount
);
1525 // Use coverage weighting to calculate the average.
1526 CalculateDfWeights (hData
, weights
);
1528 // If coverage weighting is not used, the weights still need to be
1529 // averaged by the number of HRIRs.
1531 for (ei
= hData
-> mEvStart
; ei
< hData
-> mEvCount
; ei
++)
1532 count
+= hData
-> mAzCount
[ei
];
1533 for (ei
= hData
-> mEvStart
; ei
< hData
-> mEvCount
; ei
++)
1534 weights
[ei
] = 1.0 / count
;
1536 ei
= hData
-> mEvStart
;
1538 step
= hData
-> mIrSize
;
1539 start
= hData
-> mEvOffset
[ei
] * step
;
1540 end
= hData
-> mIrCount
* step
;
1541 m
= 1 + (hData
-> mFftSize
/ 2);
1542 for (i
= 0; i
< m
; i
++)
1544 for (j
= start
; j
< end
; j
+= step
) {
1545 // Get the weight for this HRIR's contribution.
1546 weight
= weights
[ei
];
1547 // Add this HRIR's weighted power average to the total.
1548 for (i
= 0; i
< m
; i
++)
1549 dfa
[i
] += weight
* hData
-> mHrirs
[j
+ i
] * hData
-> mHrirs
[j
+ i
];
1550 // Determine the next weight to use.
1552 if (ai
>= hData
-> mAzCount
[ei
]) {
1557 // Finish the average calculation and keep it from being too small.
1558 for (i
= 0; i
< m
; i
++)
1559 dfa
[i
] = fmax (sqrt (dfa
[i
]), EPSILON
);
1560 // Apply a limit to the magnitude range of the diffuse-field average if
1563 LimitMagnitudeResponse (hData
-> mFftSize
, limit
, dfa
, dfa
);
1564 DestroyArray (weights
);
1567 // Perform diffuse-field equalization on the magnitude responses of the HRIR
1568 // set using the given average response.
1569 static void DiffuseFieldEqualize (const double * dfa
, const HrirDataT
* hData
) {
1570 size_t step
, start
, end
, m
, j
, i
;
1572 step
= hData
-> mIrSize
;
1573 start
= hData
-> mEvOffset
[hData
-> mEvStart
] * step
;
1574 end
= hData
-> mIrCount
* step
;
1575 m
= 1 + (hData
-> mFftSize
/ 2);
1576 for (j
= start
; j
< end
; j
+= step
) {
1577 for (i
= 0; i
< m
; i
++)
1578 hData
-> mHrirs
[j
+ i
] /= dfa
[i
];
1582 // Perform minimum-phase reconstruction using the magnitude responses of the
1584 static void ReconstructHrirs (const HrirDataT
* hData
) {
1585 double * re
= NULL
, * im
= NULL
;
1586 size_t step
, start
, end
, n
, j
, i
;
1588 step
= hData
-> mIrSize
;
1589 start
= hData
-> mEvOffset
[hData
-> mEvStart
] * step
;
1590 end
= hData
-> mIrCount
* step
;
1591 n
= hData
-> mFftSize
;
1592 re
= CreateArray (n
);
1593 im
= CreateArray (n
);
1594 for (j
= start
; j
< end
; j
+= step
) {
1595 MinimumPhase (n
, & hData
-> mHrirs
[j
], re
, im
);
1596 FftInverse (n
, re
, im
, re
, im
);
1597 for (i
= 0; i
< hData
-> mIrPoints
; i
++)
1598 hData
-> mHrirs
[j
+ i
] = re
[i
];
1604 /* Given an elevation index and an azimuth, calculate the indices of the two
1605 * HRIRs that bound the coordinate along with a factor for calculating the
1606 * continous HRIR using interpolation.
1608 static void CalcAzIndices (const HrirDataT
* hData
, const uint ei
, const double az
, uint
* j0
, uint
* j1
, double * jf
) {
1612 af
= ((2.0 * M_PI
) + az
) * hData
-> mAzCount
[ei
] / (2.0 * M_PI
);
1613 ai
= ((uint
) af
) % hData
-> mAzCount
[ei
];
1615 (* j0
) = hData
-> mEvOffset
[ei
] + ai
;
1616 (* j1
) = hData
-> mEvOffset
[ei
] + ((ai
+ 1) % hData
-> mAzCount
[ei
]);
1620 /* Attempt to synthesize any missing HRIRs at the bottom elevations. Right
1621 * now this just blends the lowest elevation HRIRs together and applies some
1622 * attenuation and high frequency damping. It is a simple, if inaccurate
1625 static void SynthesizeHrirs (HrirDataT
* hData
) {
1627 size_t step
, n
, i
, j
;
1631 double lp
[4], s0
, s1
;
1633 if (hData
-> mEvStart
<= 0)
1635 step
= hData
-> mIrSize
;
1636 oi
= hData
-> mEvStart
;
1637 n
= hData
-> mIrPoints
;
1638 for (i
= 0; i
< n
; i
++)
1639 hData
-> mHrirs
[i
] = 0.0;
1640 for (a
= 0; a
< hData
-> mAzCount
[oi
]; a
++) {
1641 j
= (hData
-> mEvOffset
[oi
] + a
) * step
;
1642 for (i
= 0; i
< n
; i
++)
1643 hData
-> mHrirs
[i
] += hData
-> mHrirs
[j
+ i
] / hData
-> mAzCount
[oi
];
1645 for (e
= 1; e
< hData
-> mEvStart
; e
++) {
1646 of
= ((double) e
) / hData
-> mEvStart
;
1647 for (a
= 0; a
< hData
-> mAzCount
[e
]; a
++) {
1648 j
= (hData
-> mEvOffset
[e
] + a
) * step
;
1649 CalcAzIndices (hData
, oi
, a
* 2.0 * M_PI
/ hData
-> mAzCount
[e
], & j0
, & j1
, & jf
);
1656 for (i
= 0; i
< n
; i
++) {
1657 s0
= hData
-> mHrirs
[i
];
1658 s1
= Lerp (hData
-> mHrirs
[j0
+ i
], hData
-> mHrirs
[j1
+ i
], jf
);
1659 s0
= Lerp (s0
, s1
, of
);
1660 lp
[0] = Lerp (s0
, lp
[0], 0.15 - (0.15 * of
));
1661 lp
[1] = Lerp (lp
[0], lp
[1], 0.15 - (0.15 * of
));
1662 lp
[2] = Lerp (lp
[1], lp
[2], 0.15 - (0.15 * of
));
1663 lp
[3] = Lerp (lp
[2], lp
[3], 0.15 - (0.15 * of
));
1664 hData
-> mHrirs
[j
+ i
] = lp
[3];
1672 for (i
= 0; i
< n
; i
++) {
1673 s0
= hData
-> mHrirs
[i
];
1674 lp
[0] = Lerp (s0
, lp
[0], 0.15);
1675 lp
[1] = Lerp (lp
[0], lp
[1], 0.15);
1676 lp
[2] = Lerp (lp
[1], lp
[2], 0.15);
1677 lp
[3] = Lerp (lp
[2], lp
[3], 0.15);
1678 hData
-> mHrirs
[i
] = lp
[3];
1680 hData
-> mEvStart
= 0;
1683 // The following routines assume a full set of HRIRs for all elevations.
1685 // Normalize the HRIR set and slightly attenuate the result.
1686 static void NormalizeHrirs (const HrirDataT
* hData
) {
1687 size_t step
, end
, n
, j
, i
;
1690 step
= hData
-> mIrSize
;
1691 end
= hData
-> mIrCount
* step
;
1692 n
= hData
-> mIrPoints
;
1694 for (j
= 0; j
< end
; j
+= step
) {
1695 for (i
= 0; i
< n
; i
++)
1696 maxLevel
= fmax (fabs (hData
-> mHrirs
[j
+ i
]), maxLevel
);
1698 maxLevel
= 1.01 * maxLevel
;
1699 for (j
= 0; j
< end
; j
+= step
) {
1700 for (i
= 0; i
< n
; i
++)
1701 hData
-> mHrirs
[j
+ i
] /= maxLevel
;
1705 // Calculate the left-ear time delay using a spherical head model.
1706 static double CalcLTD (const double ev
, const double az
, const double rad
, const double dist
) {
1707 double azp
, dlp
, l
, al
;
1709 azp
= asin (cos (ev
) * sin (az
));
1710 dlp
= sqrt ((dist
* dist
) + (rad
* rad
) + (2.0 * dist
* rad
* sin (azp
)));
1711 l
= sqrt ((dist
* dist
) - (rad
* rad
));
1712 al
= (0.5 * M_PI
) + azp
;
1714 dlp
= l
+ (rad
* (al
- acos (rad
/ dist
)));
1715 return (dlp
/ 343.3);
1718 // Calculate the effective head-related time delays for the each HRIR, now
1719 // that they are minimum-phase.
1720 static void CalculateHrtds (HrirDataT
* hData
) {
1721 double minHrtd
, maxHrtd
;
1727 for (e
= 0; e
< hData
-> mEvCount
; e
++) {
1728 for (a
= 0; a
< hData
-> mAzCount
[e
]; a
++) {
1729 j
= hData
-> mEvOffset
[e
] + a
;
1730 t
= CalcLTD ((-90.0 + (e
* 180.0 / (hData
-> mEvCount
- 1))) * M_PI
/ 180.0,
1731 (a
* 360.0 / hData
-> mAzCount
[e
]) * M_PI
/ 180.0,
1732 hData
-> mRadius
, hData
-> mDistance
);
1733 hData
-> mHrtds
[j
] = t
;
1734 maxHrtd
= fmax (t
, maxHrtd
);
1735 minHrtd
= fmin (t
, minHrtd
);
1739 for (j
= 0; j
< hData
-> mIrCount
; j
++)
1740 hData
-> mHrtds
[j
] -= minHrtd
;
1741 hData
-> mMaxHrtd
= maxHrtd
;
1744 // Store the OpenAL Soft HRTF data set.
1745 static int StoreMhr (const HrirDataT
* hData
, const char * filename
) {
1748 size_t step
, end
, n
, j
, i
;
1751 if ((fp
= fopen (filename
, "wb")) == NULL
) {
1752 fprintf (stderr
, "Error: Could not open MHR file '%s'.\n", filename
);
1755 if (! WriteAscii (MHR_FORMAT
, fp
, filename
))
1757 if (! WriteBin4 (BO_LITTLE
, 4, (uint4
) hData
-> mIrRate
, fp
, filename
))
1759 if (! WriteBin4 (BO_LITTLE
, 1, (uint4
) hData
-> mIrPoints
, fp
, filename
))
1761 if (! WriteBin4 (BO_LITTLE
, 1, (uint4
) hData
-> mEvCount
, fp
, filename
))
1763 for (e
= 0; e
< hData
-> mEvCount
; e
++) {
1764 if (! WriteBin4 (BO_LITTLE
, 1, (uint4
) hData
-> mAzCount
[e
], fp
, filename
))
1767 step
= hData
-> mIrSize
;
1768 end
= hData
-> mIrCount
* step
;
1769 n
= hData
-> mIrPoints
;
1771 for (j
= 0; j
< end
; j
+= step
) {
1773 for (i
= 0; i
< n
; i
++) {
1774 v
= HpTpdfDither (32767.0 * hData
-> mHrirs
[j
+ i
], & hpHist
);
1775 if (! WriteBin4 (BO_LITTLE
, 2, (uint4
) v
, fp
, filename
))
1779 for (j
= 0; j
< hData
-> mIrCount
; j
++) {
1780 v
= (int) fmin (round (hData
-> mIrRate
* hData
-> mHrtds
[j
]), MAX_HRTD
);
1781 if (! WriteBin4 (BO_LITTLE
, 1, (uint4
) v
, fp
, filename
))
1788 // Store the OpenAL Soft built-in table.
1789 static int StoreTable (const HrirDataT
* hData
, const char * filename
) {
1791 size_t step
, end
, n
, j
, i
;
1793 char text
[128 + 1];
1795 if ((fp
= fopen (filename
, "wb")) == NULL
) {
1796 fprintf (stderr
, "Error: Could not open table file '%s'.\n", filename
);
1799 snprintf (text
, 128, "/* Elevation metrics */\n"
1800 "static const ALubyte defaultAzCount[%u] = { ", hData
-> mEvCount
);
1801 if (! WriteAscii (text
, fp
, filename
))
1803 for (i
= 0; i
< hData
-> mEvCount
; i
++) {
1804 snprintf (text
, 128, "%u, ", hData
-> mAzCount
[i
]);
1805 if (! WriteAscii (text
, fp
, filename
))
1808 snprintf (text
, 128, "};\n"
1809 "static const ALushort defaultEvOffset[%u] = { ", hData
-> mEvCount
);
1810 if (! WriteAscii (text
, fp
, filename
))
1812 for (i
= 0; i
< hData
-> mEvCount
; i
++) {
1813 snprintf (text
, 128, "%u, ", hData
-> mEvOffset
[i
]);
1814 if (! WriteAscii (text
, fp
, filename
))
1817 step
= hData
-> mIrSize
;
1818 end
= hData
-> mIrCount
* step
;
1819 n
= hData
-> mIrPoints
;
1820 snprintf (text
, 128, "};\n\n"
1821 "/* HRIR Coefficients */\n"
1822 "static const ALshort defaultCoeffs[%u] =\n{\n", hData
-> mIrCount
* n
);
1823 if (! WriteAscii (text
, fp
, filename
))
1826 for (j
= 0; j
< end
; j
+= step
) {
1827 if (! WriteAscii (" ", fp
, filename
))
1830 for (i
= 0; i
< n
; i
++) {
1831 v
= HpTpdfDither (32767.0 * hData
-> mHrirs
[j
+ i
], & hpHist
);
1832 snprintf (text
, 128, "%+d, ", v
);
1833 if (! WriteAscii (text
, fp
, filename
))
1836 if (! WriteAscii ("\n", fp
, filename
))
1839 snprintf (text
, 128, "};\n\n"
1840 "/* HRIR Delays */\n"
1841 "static const ALubyte defaultDelays[%d] =\n{\n"
1842 " ", hData
-> mIrCount
);
1843 if (! WriteAscii (text
, fp
, filename
))
1845 for (j
= 0; j
< hData
-> mIrCount
; j
++) {
1846 v
= (int) fmin (round (hData
-> mIrRate
* hData
-> mHrtds
[j
]), MAX_HRTD
);
1847 snprintf (text
, 128, "%d, ", v
);
1848 if (! WriteAscii (text
, fp
, filename
))
1851 if (! WriteAscii ("\n};\n\n"
1852 "/* Default HRTF Definition */\n", fp
, filename
))
1854 snprintf (text
, 128, "static const struct Hrtf DefaultHrtf = {\n"
1855 " %u, %u, %u, defaultAzCount, defaultEvOffset,\n",
1856 hData
-> mIrRate
, hData
-> mIrPoints
, hData
-> mEvCount
);
1857 if (! WriteAscii (text
, fp
, filename
))
1859 if (! WriteAscii (" defaultCoeffs, defaultDelays, NULL\n"
1860 "};\n", fp
, filename
))
1866 // Process the data set definition to read and validate the data set metrics.
1867 static int ProcessMetrics (TokenReaderT
* tr
, const size_t fftSize
, const size_t truncSize
, HrirDataT
* hData
) {
1868 char ident
[MAX_IDENT_LEN
+ 1];
1873 int hasRate
= 0, hasPoints
= 0, hasAzimuths
= 0;
1874 int hasRadius
= 0, hasDistance
= 0;
1876 while (! (hasRate
&& hasPoints
&& hasAzimuths
&& hasRadius
&& hasDistance
)) {
1877 TrIndication (tr
, & line
, & col
);
1878 if (! TrReadIdent (tr
, MAX_IDENT_LEN
, ident
))
1880 if (strcasecmp (ident
, "rate") == 0) {
1882 TrErrorAt (tr
, line
, col
, "Redefinition of 'rate'.\n");
1885 if (! TrReadOperator (tr
, "="))
1887 if (! TrReadInt (tr
, MIN_RATE
, MAX_RATE
, & intVal
))
1889 hData
-> mIrRate
= intVal
;
1891 } else if (strcasecmp (ident
, "points") == 0) {
1893 TrErrorAt (tr
, line
, col
, "Redefinition of 'points'.\n");
1896 if (! TrReadOperator (tr
, "="))
1898 TrIndication (tr
, & line
, & col
);
1899 if (! TrReadInt (tr
, MIN_POINTS
, MAX_POINTS
, & intVal
))
1901 points
= (size_t) intVal
;
1902 if ((fftSize
> 0) && (points
> fftSize
)) {
1903 TrErrorAt (tr
, line
, col
, "Value exceeds the overriden FFT size.\n");
1906 if (points
< truncSize
) {
1907 TrErrorAt (tr
, line
, col
, "Value is below the truncation size.\n");
1910 hData
-> mIrPoints
= points
;
1911 hData
-> mFftSize
= fftSize
;
1914 while (points
< (4 * hData
-> mIrPoints
))
1916 hData
-> mFftSize
= points
;
1917 hData
-> mIrSize
= 1 + (points
/ 2);
1919 hData
-> mFftSize
= fftSize
;
1920 hData
-> mIrSize
= 1 + (fftSize
/ 2);
1921 if (points
> hData
-> mIrSize
)
1922 hData
-> mIrSize
= points
;
1925 } else if (strcasecmp (ident
, "azimuths") == 0) {
1927 TrErrorAt (tr
, line
, col
, "Redefinition of 'azimuths'.\n");
1930 if (! TrReadOperator (tr
, "="))
1932 hData
-> mIrCount
= 0;
1933 hData
-> mEvCount
= 0;
1934 hData
-> mEvOffset
[0] = 0;
1936 if (! TrReadInt (tr
, MIN_AZ_COUNT
, MAX_AZ_COUNT
, & intVal
))
1938 hData
-> mAzCount
[hData
-> mEvCount
] = intVal
;
1939 hData
-> mIrCount
+= intVal
;
1940 hData
-> mEvCount
++;
1941 if (! TrIsOperator (tr
, ","))
1943 if (hData
-> mEvCount
>= MAX_EV_COUNT
) {
1944 TrError (tr
, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT
);
1947 hData
-> mEvOffset
[hData
-> mEvCount
] = hData
-> mEvOffset
[hData
-> mEvCount
- 1] + intVal
;
1948 TrReadOperator (tr
, ",");
1950 if (hData
-> mEvCount
< MIN_EV_COUNT
) {
1951 TrErrorAt (tr
, line
, col
, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT
);
1955 } else if (strcasecmp (ident
, "radius") == 0) {
1957 TrErrorAt (tr
, line
, col
, "Redefinition of 'radius'.\n");
1960 if (! TrReadOperator (tr
, "="))
1962 if (! TrReadFloat (tr
, MIN_RADIUS
, MAX_RADIUS
, & fpVal
))
1964 hData
-> mRadius
= fpVal
;
1966 } else if (strcasecmp (ident
, "distance") == 0) {
1968 TrErrorAt (tr
, line
, col
, "Redefinition of 'distance'.\n");
1971 if (! TrReadOperator (tr
, "="))
1973 if (! TrReadFloat (tr
, MIN_DISTANCE
, MAX_DISTANCE
, & fpVal
))
1975 hData
-> mDistance
= fpVal
;
1978 TrErrorAt (tr
, line
, col
, "Expected a metric name.\n");
1981 TrSkipWhitespace (tr
);
1986 // Parse an index pair from the data set definition.
1987 static int ReadIndexPair (TokenReaderT
* tr
, const HrirDataT
* hData
, uint
* ei
, uint
* ai
) {
1990 if (! TrReadInt (tr
, 0, hData
-> mEvCount
, & intVal
))
1992 (* ei
) = (uint
) intVal
;
1993 if (! TrReadOperator (tr
, ","))
1995 if (! TrReadInt (tr
, 0, hData
-> mAzCount
[(* ei
)], & intVal
))
1997 (* ai
) = (uint
) intVal
;
2001 // Match the source format from a given identifier.
2002 static SourceFormatT
MatchSourceFormat (const char * ident
) {
2003 if (strcasecmp (ident
, "wave") == 0)
2005 else if (strcasecmp (ident
, "bin_le") == 0)
2007 else if (strcasecmp (ident
, "bin_be") == 0)
2009 else if (strcasecmp (ident
, "ascii") == 0)
2014 // Match the source element type from a given identifier.
2015 static ElementTypeT
MatchElementType (const char * ident
) {
2016 if (strcasecmp (ident
, "int") == 0)
2018 else if (strcasecmp (ident
, "fp") == 0)
2023 // Parse and validate a source reference from the data set definition.
2024 static int ReadSourceRef (TokenReaderT
* tr
, SourceRefT
* src
) {
2026 char ident
[MAX_IDENT_LEN
+ 1];
2029 TrIndication (tr
, & line
, & col
);
2030 if (! TrReadIdent (tr
, MAX_IDENT_LEN
, ident
))
2032 src
-> mFormat
= MatchSourceFormat (ident
);
2033 if (src
-> mFormat
== SF_NONE
) {
2034 TrErrorAt (tr
, line
, col
, "Expected a source format.\n");
2037 if (! TrReadOperator (tr
, "("))
2039 if (src
-> mFormat
== SF_WAVE
) {
2040 if (! TrReadInt (tr
, 0, MAX_WAVE_CHANNELS
, & intVal
))
2042 src
-> mType
= ET_NONE
;
2045 src
-> mChannel
= (uint
) intVal
;
2048 TrIndication (tr
, & line
, & col
);
2049 if (! TrReadIdent (tr
, MAX_IDENT_LEN
, ident
))
2051 src
-> mType
= MatchElementType (ident
);
2052 if (src
-> mType
== ET_NONE
) {
2053 TrErrorAt (tr
, line
, col
, "Expected a source element type.\n");
2056 if ((src
-> mFormat
== SF_BIN_LE
) || (src
-> mFormat
== SF_BIN_BE
)) {
2057 if (! TrReadOperator (tr
, ","))
2059 if (src
-> mType
== ET_INT
) {
2060 if (! TrReadInt (tr
, MIN_BIN_SIZE
, MAX_BIN_SIZE
, & intVal
))
2062 src
-> mSize
= intVal
;
2063 if (TrIsOperator (tr
, ",")) {
2064 TrReadOperator (tr
, ",");
2065 TrIndication (tr
, & line
, & col
);
2066 if (! TrReadInt (tr
, 0x80000000, 0x7FFFFFFF, & intVal
))
2068 if ((abs (intVal
) < MIN_BIN_BITS
) || ((uint
)abs(intVal
) > (8 * src
-> mSize
))) {
2069 TrErrorAt (tr
, line
, col
, "Expected a value of (+/-) %d to %d.\n", MIN_BIN_BITS
, 8 * src
-> mSize
);
2072 src
-> mBits
= intVal
;
2074 src
-> mBits
= 8 * src
-> mSize
;
2077 TrIndication (tr
, & line
, & col
);
2078 if (! TrReadInt (tr
, 0x80000000, 0x7FFFFFFF, & intVal
))
2080 if ((intVal
!= 4) && (intVal
!= 8)) {
2081 TrErrorAt (tr
, line
, col
, "Expected a value of 4 or 8.\n");
2084 src
-> mSize
= (uint
) intVal
;
2087 } else if ((src
-> mFormat
== SF_ASCII
) && (src
-> mType
== ET_INT
)) {
2088 if (! TrReadOperator (tr
, ","))
2090 if (! TrReadInt (tr
, MIN_ASCII_BITS
, MAX_ASCII_BITS
, & intVal
))
2093 src
-> mBits
= intVal
;
2098 if (TrIsOperator (tr
, ";")) {
2099 TrReadOperator (tr
, ";");
2100 if (! TrReadInt (tr
, 0, 0x7FFFFFFF, & intVal
))
2102 src
-> mSkip
= (uint
) intVal
;
2107 if (! TrReadOperator (tr
, ")"))
2109 if (TrIsOperator (tr
, "@")) {
2110 TrReadOperator (tr
, "@");
2111 if (! TrReadInt (tr
, 0, 0x7FFFFFFF, & intVal
))
2113 src
-> mOffset
= (uint
) intVal
;
2117 if (! TrReadOperator (tr
, ":"))
2119 if (! TrReadString (tr
, MAX_PATH_LEN
, src
-> mPath
))
2124 // Process the list of sources in the data set definition.
2125 static int ProcessSources (TokenReaderT
* tr
, HrirDataT
* hData
) {
2126 uint
* setCount
= NULL
, * setFlag
= NULL
;
2127 double * hrir
= NULL
;
2128 uint line
, col
, ei
, ai
;
2132 setCount
= (uint
*) calloc (hData
-> mEvCount
, sizeof (uint
));
2133 setFlag
= (uint
*) calloc (hData
-> mIrCount
, sizeof (uint
));
2134 hrir
= CreateArray (hData
-> mIrPoints
);
2135 while (TrIsOperator (tr
, "[")) {
2136 TrIndication (tr
, & line
, & col
);
2137 TrReadOperator (tr
, "[");
2138 if (ReadIndexPair (tr
, hData
, & ei
, & ai
)) {
2139 if (TrReadOperator (tr
, "]")) {
2140 if (! setFlag
[hData
-> mEvOffset
[ei
] + ai
]) {
2141 if (TrReadOperator (tr
, "=")) {
2144 if (ReadSourceRef (tr
, & src
)) {
2145 if (LoadSource (& src
, hData
-> mIrRate
, hData
-> mIrPoints
, hrir
)) {
2146 AverageHrirMagnitude (hrir
, 1.0 / factor
, ei
, ai
, hData
);
2148 if (! TrIsOperator (tr
, "+"))
2150 TrReadOperator (tr
, "+");
2154 DestroyArray (hrir
);
2159 setFlag
[hData
-> mEvOffset
[ei
] + ai
] = 1;
2164 TrErrorAt (tr
, line
, col
, "Redefinition of source.\n");
2168 DestroyArray (hrir
);
2174 while ((ei
< hData
-> mEvCount
) && (setCount
[ei
] < 1))
2176 if (ei
< hData
-> mEvCount
) {
2177 hData
-> mEvStart
= ei
;
2178 while ((ei
< hData
-> mEvCount
) && (setCount
[ei
] == hData
-> mAzCount
[ei
]))
2180 if (ei
>= hData
-> mEvCount
) {
2181 if (! TrLoad (tr
)) {
2182 DestroyArray (hrir
);
2187 TrError (tr
, "Errant data at end of source list.\n");
2190 TrError (tr
, "Missing sources for elevation index %d.\n", ei
);
2193 TrError (tr
, "Missing source references.\n");
2195 DestroyArray (hrir
);
2201 /* Parse the data set definition and process the source data, storing the
2202 * resulting data set as desired. If the input name is NULL it will read
2203 * from standard input.
2205 static int ProcessDefinition (const char * inName
, const size_t fftSize
, const int equalize
, const int surface
, const double limit
, const size_t truncSize
, const OutputFormatT outFormat
, const char * outName
) {
2209 double * dfa
= NULL
;
2210 char rateStr
[8 + 1], expName
[MAX_PATH_LEN
];
2213 hData
.mIrPoints
= 0;
2219 hData
.mDistance
= 0;
2221 fprintf (stdout
, "Reading HRIR definition...\n");
2222 if (inName
!= NULL
) {
2223 fp
= fopen (inName
, "r");
2225 fprintf (stderr
, "Error: Could not open definition file '%s'\n", inName
);
2228 TrSetup (fp
, inName
, & tr
);
2231 TrSetup (fp
, "<stdin>", & tr
);
2233 if (! ProcessMetrics (& tr
, fftSize
, truncSize
, & hData
)) {
2238 hData
. mHrirs
= CreateArray (hData
. mIrCount
* hData
. mIrSize
);
2239 hData
. mHrtds
= CreateArray (hData
. mIrCount
);
2240 if (! ProcessSources (& tr
, & hData
)) {
2241 DestroyArray (hData
. mHrtds
);
2242 DestroyArray (hData
. mHrirs
);
2250 dfa
= CreateArray (1 + (hData
. mFftSize
/ 2));
2251 fprintf (stdout
, "Calculating diffuse-field average...\n");
2252 CalculateDiffuseFieldAverage (& hData
, surface
, limit
, dfa
);
2253 fprintf (stdout
, "Performing diffuse-field equalization...\n");
2254 DiffuseFieldEqualize (dfa
, & hData
);
2257 fprintf (stdout
, "Performing minimum phase reconstruction...\n");
2258 ReconstructHrirs (& hData
);
2259 fprintf (stdout
, "Truncating minimum-phase HRIRs...\n");
2260 hData
. mIrPoints
= truncSize
;
2261 fprintf (stdout
, "Synthesizing missing elevations...\n");
2262 SynthesizeHrirs (& hData
);
2263 fprintf (stdout
, "Normalizing final HRIRs...\n");
2264 NormalizeHrirs (& hData
);
2265 fprintf (stdout
, "Calculating impulse delays...\n");
2266 CalculateHrtds (& hData
);
2267 snprintf (rateStr
, 8, "%u", hData
. mIrRate
);
2268 StrSubst (outName
, "%r", rateStr
, MAX_PATH_LEN
, expName
);
2269 switch (outFormat
) {
2271 fprintf (stdout
, "Creating MHR data set file...\n");
2272 if (! StoreMhr (& hData
, expName
))
2276 fprintf (stderr
, "Creating OpenAL Soft table file...\n");
2277 if (! StoreTable (& hData
, expName
))
2283 DestroyArray (hData
. mHrtds
);
2284 DestroyArray (hData
. mHrirs
);
2288 // Standard command line dispatch.
2289 int main (const int argc
, const char * argv
[]) {
2290 const char * inName
= NULL
, * outName
= NULL
;
2291 OutputFormatT outFormat
;
2294 int equalize
, surface
;
2300 fprintf (stderr
, "Error: No command specified. See '%s -h' for help.\n", argv
[0]);
2303 if ((strcmp (argv
[1], "--help") == 0) || (strcmp (argv
[1], "-h") == 0)) {
2304 fprintf (stdout
, "HRTF Processing and Composition Utility\n\n");
2305 fprintf (stdout
, "Usage: %s <command> [<option>...]\n\n", argv
[0]);
2306 fprintf (stdout
, "Commands:\n");
2307 fprintf (stdout
, " -m, --make-mhr Makes an OpenAL Soft compatible HRTF data set.\n");
2308 fprintf (stdout
, " Defaults output to: ./oalsoft_hrtf_%%r.mhr\n");
2309 fprintf (stdout
, " -t, --make-tab Makes the built-in table used when compiling OpenAL Soft.\n");
2310 fprintf (stdout
, " Defaults output to: ./hrtf_tables.inc\n");
2311 fprintf (stdout
, " -h, --help Displays this help information.\n\n");
2312 fprintf (stdout
, "Options:\n");
2313 fprintf (stdout
, " -f=<points> Override the FFT window size (defaults to the first power-\n");
2314 fprintf (stdout
, " of-two that fits four times the number of HRIR points).\n");
2315 fprintf (stdout
, " -e={on|off} Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE
? "on" : "off"));
2316 fprintf (stdout
, " -s={on|off} Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE
? "on" : "off"));
2317 fprintf (stdout
, " -l={<dB>|none} Specify a limit to the magnitude range of the diffuse-field\n");
2318 fprintf (stdout
, " average (default: %.2f).\n", DEFAULT_LIMIT
);
2319 fprintf (stdout
, " -w=<points> Specify the size of the truncation window that's applied\n");
2320 fprintf (stdout
, " after minimum-phase reconstruction (default: %d).\n", DEFAULT_TRUNCSIZE
);
2321 fprintf (stdout
, " -i=<filename> Specify an HRIR definition file to use (defaults to stdin).\n");
2322 fprintf (stdout
, " -o=<filename> Specify an output file. Overrides command-selected default.\n");
2323 fprintf (stdout
, " Use of '%%r' will be substituted with the data set sample rate.\n");
2326 if ((strcmp (argv
[1], "--make-mhr") == 0) || (strcmp (argv
[1], "-m") == 0)) {
2330 outName
= "./oalsoft_hrtf_%r.mhr";
2332 } else if ((strcmp (argv
[1], "--make-tab") == 0) || (strcmp (argv
[1], "-t") == 0)) {
2336 outName
= "./hrtf_tables.inc";
2337 outFormat
= OF_TABLE
;
2339 fprintf (stderr
, "Error: Invalid command '%s'.\n", argv
[1]);
2344 equalize
= DEFAULT_EQUALIZE
;
2345 surface
= DEFAULT_SURFACE
;
2346 limit
= DEFAULT_LIMIT
;
2347 truncSize
= DEFAULT_TRUNCSIZE
;
2348 while (argi
< argc
) {
2349 if (strncmp (argv
[argi
], "-f=", 3) == 0) {
2350 fftSize
= strtoul (& argv
[argi
] [3], & end
, 10);
2351 if ((end
[0] != '\0') || (fftSize
& (fftSize
- 1)) || (fftSize
< MIN_FFTSIZE
) || (fftSize
> MAX_FFTSIZE
)) {
2352 fprintf (stderr
, "Error: Expected a power-of-two value from %d to %d for '-f'.\n", MIN_FFTSIZE
, MAX_FFTSIZE
);
2355 } else if (strncmp (argv
[argi
], "-e=", 3) == 0) {
2356 if (strcmp (& argv
[argi
] [3], "on") == 0) {
2358 } else if (strcmp (& argv
[argi
] [3], "off") == 0) {
2361 fprintf (stderr
, "Error: Expected 'on' or 'off' for '-e'.\n");
2364 } else if (strncmp (argv
[argi
], "-s=", 3) == 0) {
2365 if (strcmp (& argv
[argi
] [3], "on") == 0) {
2367 } else if (strcmp (& argv
[argi
] [3], "off") == 0) {
2370 fprintf (stderr
, "Error: Expected 'on' or 'off' for '-s'.\n");
2373 } else if (strncmp (argv
[argi
], "-l=", 3) == 0) {
2374 if (strcmp (& argv
[argi
] [3], "none") == 0) {
2377 limit
= strtod (& argv
[argi
] [3], & end
);
2378 if ((end
[0] != '\0') || (limit
< MIN_LIMIT
) || (limit
> MAX_LIMIT
)) {
2379 fprintf (stderr
, "Error: Expected 'none' or a value from %.2f to %.2f for '-l'.\n", MIN_LIMIT
, MAX_LIMIT
);
2383 } else if (strncmp (argv
[argi
], "-w=", 3) == 0) {
2384 truncSize
= strtoul (& argv
[argi
] [3], & end
, 10);
2385 if ((end
[0] != '\0') || (truncSize
< MIN_TRUNCSIZE
) || (truncSize
> MAX_TRUNCSIZE
) || (truncSize
% MOD_TRUNCSIZE
)) {
2386 fprintf (stderr
, "Error: Expected a value from %d to %d in multiples of %d for '-w'.\n", MIN_TRUNCSIZE
, MAX_TRUNCSIZE
, MOD_TRUNCSIZE
);
2389 } else if (strncmp (argv
[argi
], "-i=", 3) == 0) {
2390 inName
= & argv
[argi
] [3];
2391 } else if (strncmp (argv
[argi
], "-o=", 3) == 0) {
2392 outName
= & argv
[argi
] [3];
2394 fprintf (stderr
, "Error: Invalid option '%s'.\n", argv
[argi
]);
2399 if (! ProcessDefinition (inName
, fftSize
, equalize
, surface
, limit
, truncSize
, outFormat
, outName
))
2401 fprintf (stdout
, "Operation completed.\n");