2 * HRTF utility for producing and demonstrating the process of creating an
3 * OpenAL Soft compatible HRIR data set.
5 * It can currently make use of the 44.1 KHz diffuse and compact KEMAR HRIRs
8 * http://sound.media.mit.edu/resources/KEMAR.html
19 #define M_PI 3.14159265358979323846
23 static double round(double val
)
26 return ceil(val
- 0.5);
27 return floor(val
+ 0.5);
31 // The sample rate of the MIT HRIR data sets.
32 #define MIT_IR_RATE (44100)
34 // The total number of used impulse responses from the MIT HRIR data sets.
35 #define MIT_IR_COUNT (828)
37 // The size (in samples) of each HRIR in the MIT data sets.
38 #define MIT_IR_SIZE (128)
40 // The total number of elevations given a step of 10 degrees.
41 #define MIT_EV_COUNT (19)
43 // The first elevation that the MIT data sets have HRIRs for.
44 #define MIT_EV_START (5)
46 // The head radius (in meters) used by the MIT data sets.
47 #define MIT_RADIUS (0.09f)
49 // The source to listener distance (in meters) used by the MIT data sets.
50 #define MIT_DISTANCE (1.4f)
52 // The resulting size (in samples) of a mininum-phase reconstructed HRIR.
53 #define MIN_IR_SIZE (32)
55 // The size (in samples) of the real cepstrum used in reconstruction. This
56 // needs to be large enough to reduce inaccuracy.
57 #define CEP_SIZE (8192)
59 // The OpenAL Soft HRTF format marker. It stands for minimum-phase head
60 // response protocol 00.
61 #define MHR_FORMAT ("MinPHR00")
63 typedef struct ComplexT ComplexT
;
64 typedef struct HrirDataT HrirDataT
;
66 // A complex number type.
71 // The HRIR data definition. This can be used to add support for new HRIR
72 // sources in the future.
79 const int * mEvOffset
,
88 // The linear index of the first HRIR for each elevation of the MIT data set.
89 static const int MIT_EV_OFFSET
[MIT_EV_COUNT
] = {
90 0, 1, 13, 37, 73, 118, 174, 234, 306, 378, 450, 522, 594, 654, 710, 755, 791, 815, 827
93 // The count of distinct azimuth steps for each elevation in the MIT data
95 MIT_AZ_COUNT
[MIT_EV_COUNT
] = {
96 1, 12, 24, 36, 45, 56, 60, 72, 72, 72, 72, 72, 60, 56, 45, 36, 24, 12, 1
99 // Performs a forward Fast Fourier Transform.
100 static void FftProc (int n
, const ComplexT
* fftIn
, ComplexT
* fftOut
) {
108 // Data copy and bit-reversal ordering.
111 for (k
= 0; k
< n
; k
++) {
112 fftOut
[rk
] . mVec
[0] = fftIn
[k
] . mVec
[0];
113 fftOut
[rk
] . mVec
[1] = fftIn
[k
] . mVec
[1];
125 for (m
= 2; m
<= n
; m
<<= 1) {
128 b
= sin (2.0f
* M_PI
/ m
);
129 for (i
= 0; i
< n
; i
+= m
) {
132 for (k
= i
, j
= 0; j
< m2
; k
++, j
++) {
134 tx
= (wx
* fftOut
[km2
] . mVec
[0]) - (wy
* fftOut
[km2
] . mVec
[1]);
135 ty
= (wx
* fftOut
[km2
] . mVec
[1]) + (wy
* fftOut
[km2
] . mVec
[0]);
136 fftOut
[km2
] . mVec
[0] = fftOut
[k
] . mVec
[0] - tx
;
137 fftOut
[km2
] . mVec
[1] = fftOut
[k
] . mVec
[1] - ty
;
138 fftOut
[k
] . mVec
[0] += tx
;
139 fftOut
[k
] . mVec
[1] += ty
;
140 wyd
= (a
* wy
) - (b
* wx
);
141 wx
-= (a
* wx
) + (b
* wy
);
149 // Performs an inverse Fast Fourier Transform.
150 static void FftInvProc (int n
, const ComplexT
* fftIn
, ComplexT
* fftOut
) {
156 float tx
, ty
, wyd
, invn
;
158 // Data copy and bit-reversal ordering.
161 for (k
= 0; k
< n
; k
++) {
162 fftOut
[rk
] . mVec
[0] = fftIn
[k
] . mVec
[0];
163 fftOut
[rk
] . mVec
[1] = fftIn
[k
] . mVec
[1];
175 for (m
= 2; m
<= n
; m
<<= 1) {
178 b
= -sin (2.0f
* M_PI
/ m
);
179 for (i
= 0; i
< n
; i
+= m
) {
182 for (k
= i
, j
= 0; j
< m2
; k
++, j
++) {
184 tx
= (wx
* fftOut
[km2
] . mVec
[0]) - (wy
* fftOut
[km2
] . mVec
[1]);
185 ty
= (wx
* fftOut
[km2
] . mVec
[1]) + (wy
* fftOut
[km2
] . mVec
[0]);
186 fftOut
[km2
] . mVec
[0] = fftOut
[k
] . mVec
[0] - tx
;
187 fftOut
[km2
] . mVec
[1] = fftOut
[k
] . mVec
[1] - ty
;
188 fftOut
[k
] . mVec
[0] += tx
;
189 fftOut
[k
] . mVec
[1] += ty
;
190 wyd
= (a
* wy
) - (b
* wx
);
191 wx
-= (a
* wx
) + (b
* wy
);
197 // Normalize the samples.
199 for (i
= 0; i
< n
; i
++) {
200 fftOut
[i
] . mVec
[0] *= invn
;
201 fftOut
[i
] . mVec
[1] *= invn
;
205 // Complex absolute value.
206 static void ComplexAbs (const ComplexT
* in
, ComplexT
* out
) {
207 out
-> mVec
[0] = sqrt ((in
-> mVec
[0] * in
-> mVec
[0]) + (in
-> mVec
[1] * in
-> mVec
[1]));
208 out
-> mVec
[1] = 0.0f
;
211 // Complex logarithm.
212 static void ComplexLog (const ComplexT
* in
, ComplexT
* out
) {
215 r
= sqrt ((in
-> mVec
[0] * in
-> mVec
[0]) + (in
-> mVec
[1] * in
-> mVec
[1]));
216 t
= atan2 (in
-> mVec
[1], in
-> mVec
[0]);
219 out
-> mVec
[0] = log (r
);
224 static void ComplexExp (const ComplexT
* in
, ComplexT
* out
) {
227 e
= exp (in
-> mVec
[0]);
228 out
-> mVec
[0] = e
* cos (in
-> mVec
[1]);
229 out
-> mVec
[1] = e
* sin (in
-> mVec
[1]);
232 // Calculates the real cepstrum of a given impulse response. It currently
233 // uses a fixed cepstrum size. To make this more robust, it should be
234 // rewritten to handle a variable size cepstrum.
235 static void RealCepstrum (int irSize
, const float * ir
, float cep
[CEP_SIZE
]) {
236 ComplexT in
[CEP_SIZE
], out
[CEP_SIZE
];
239 for (index
= 0; index
< irSize
; index
++) {
240 in
[index
] . mVec
[0] = ir
[index
];
241 in
[index
] . mVec
[1] = 0.0f
;
243 for (; index
< CEP_SIZE
; index
++) {
244 in
[index
] . mVec
[0] = 0.0f
;
245 in
[index
] . mVec
[1] = 0.0f
;
247 FftProc (CEP_SIZE
, in
, out
);
248 for (index
= 0; index
< CEP_SIZE
; index
++) {
249 ComplexAbs (& out
[index
], & out
[index
]);
250 if (out
[index
] . mVec
[0] < 0.000001f
)
251 out
[index
] . mVec
[0] = 0.000001f
;
252 ComplexLog (& out
[index
], & in
[index
]);
254 FftInvProc (CEP_SIZE
, in
, out
);
255 for (index
= 0; index
< CEP_SIZE
; index
++)
256 cep
[index
] = out
[index
] . mVec
[0];
259 // Reconstructs the minimum-phase impulse response for a given real cepstrum.
260 // Like the above function, this should eventually be modified to handle a
261 // variable size cepstrum.
262 static void MinimumPhase (const float cep
[CEP_SIZE
], int irSize
, float * mpIr
) {
263 ComplexT in
[CEP_SIZE
], out
[CEP_SIZE
];
266 in
[0] . mVec
[0] = cep
[0];
267 for (index
= 1; index
< (CEP_SIZE
/ 2); index
++)
268 in
[index
] . mVec
[0] = 2.0f
* cep
[index
];
269 if ((CEP_SIZE
% 2) != 1) {
270 in
[index
] . mVec
[0] = cep
[index
];
273 for (; index
< CEP_SIZE
; index
++)
274 in
[index
] . mVec
[0] = 0.0f
;
275 for (index
= 0; index
< CEP_SIZE
; index
++)
276 in
[index
] . mVec
[1] = 0.0f
;
277 FftProc (CEP_SIZE
, in
, out
);
278 for (index
= 0; index
< CEP_SIZE
; index
++)
279 ComplexExp (& out
[index
], & in
[index
]);
280 FftInvProc (CEP_SIZE
, in
, out
);
281 for (index
= 0; index
< irSize
; index
++)
282 mpIr
[index
] = out
[index
] . mVec
[0];
285 // Calculate the left-ear time delay using a spherical head model.
286 static float CalcLTD (float ev
, float az
, float rad
, float dist
) {
287 float azp
, dlp
, l
, al
;
289 azp
= asin (cos (ev
) * sin (az
));
290 dlp
= sqrt ((dist
* dist
) + (rad
* rad
) + (2.0f
* dist
* rad
* sin (azp
)));
291 l
= sqrt ((dist
* dist
) - (rad
* rad
));
292 al
= (0.5f
* M_PI
) + azp
;
294 dlp
= l
+ (rad
* (al
- acos (rad
/ dist
)));
295 return (dlp
/ 343.3f
);
298 // Read a 16-bit little-endian integer from a file and convert it to a 32-bit
299 // floating-point value in the range of -1.0 to 1.0.
300 static int ReadInt16LeAsFloat32 (const char * fileName
, FILE * fp
, float * val
) {
304 if (fread (vb
, 1, sizeof (vb
), fp
) != sizeof (vb
)) {
306 fprintf (stderr
, "Error reading from file, '%s'.\n", fileName
);
309 vw
= (((unsigned short) vb
[1]) << 8) | vb
[0];
310 (* val
) = ((short) vw
) / 32768.0f
;
314 // Write a string to a file.
315 static int WriteString (const char * val
, const char * fileName
, FILE * fp
) {
319 if (fwrite (val
, 1, len
, fp
) != len
) {
321 fprintf (stderr
, "Error writing to file, '%s'.\n", fileName
);
327 // Write a 32-bit floating-point value in the range of -1.0 to 1.0 to a file
328 // as a 16-bit little-endian integer.
329 static int WriteFloat32AsInt16Le (float val
, const char * fileName
, FILE * fp
) {
333 vw
= (short) round (32767.0f
* val
);
334 vb
[0] = vw
& 0x00FF;
335 vb
[1] = (vw
>> 8) & 0x00FF;
336 if (fwrite (vb
, 1, sizeof (vb
), fp
) != sizeof (vb
)) {
338 fprintf (stderr
, "Error writing to file, '%s'.\n", fileName
);
344 // Write a 32-bit little-endian unsigned integer to a file.
345 static int WriteUInt32Le (ALuint val
, const char * fileName
, FILE * fp
) {
348 vb
[0] = val
& 0x000000FF;
349 vb
[1] = (val
>> 8) & 0x000000FF;
350 vb
[2] = (val
>> 16) & 0x000000FF;
351 vb
[3] = (val
>> 24) & 0x000000FF;
352 if (fwrite (vb
, 1, sizeof (vb
), fp
) != sizeof (vb
)) {
354 fprintf (stderr
, "Error writing to file, '%s'.\n", fileName
);
360 // Write a 16-bit little-endian unsigned integer to a file.
361 static int WriteUInt16Le (ALushort val
, const char * fileName
, FILE * fp
) {
364 vb
[0] = val
& 0x00FF;
365 vb
[1] = (val
>> 8) & 0x00FF;
366 if (fwrite (vb
, 1, sizeof (vb
), fp
) != sizeof (vb
)) {
368 fprintf (stderr
, "Error writing to file, '%s'.\n", fileName
);
374 // Write an 8-bit unsigned integer to a file.
375 static int WriteUInt8 (ALubyte val
, const char * fileName
, FILE * fp
) {
376 if (fwrite (& val
, 1, sizeof (val
), fp
) != sizeof (val
)) {
378 fprintf (stderr
, "Error writing to file, '%s'.\n", fileName
);
384 // Load the MIT HRIRs. This loads the entire diffuse or compact set starting
385 // counter-clockwise up at the bottom elevation and clockwise at the forward
387 static int LoadMitHrirs (const char * baseName
, HrirDataT
* hData
) {
388 const int EV_ANGLE
[MIT_EV_COUNT
] = {
389 -90, -80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90
392 char fileName
[1024];
397 for (e
= MIT_EV_START
; e
< MIT_EV_COUNT
; e
++) {
398 for (a
= 0; a
< MIT_AZ_COUNT
[e
]; a
++) {
399 // The data packs the first 180 degrees in the left channel, and
400 // the last 180 degrees in the right channel.
401 if (round ((360.0f
/ MIT_AZ_COUNT
[e
]) * a
) > 180.0f
)
403 // Determine which file to open.
404 snprintf (fileName
, 1023, "%s%d/H%de%03da.wav", baseName
, EV_ANGLE
[e
], EV_ANGLE
[e
], (int) round ((360.0f
/ MIT_AZ_COUNT
[e
]) * a
));
405 if ((fp
= fopen (fileName
, "rb")) == NULL
) {
406 fprintf (stderr
, "Could not open file, '%s'.\n", fileName
);
409 // Assuming they have not changed format, skip the .WAV header.
410 fseek (fp
, 44, SEEK_SET
);
411 // Map the left and right channels to their appropriate azimuth
413 j0
= (MIT_EV_OFFSET
[e
] + a
) * MIT_IR_SIZE
;
414 j1
= (MIT_EV_OFFSET
[e
] + ((MIT_AZ_COUNT
[e
] - a
) % MIT_AZ_COUNT
[e
])) * MIT_IR_SIZE
;
415 // Read in the data, converting it to floating-point.
416 for (i
= 0; i
< MIT_IR_SIZE
; i
++) {
417 if (! ReadInt16LeAsFloat32 (fileName
, fp
, & s
))
419 hData
-> mHrirs
[j0
+ i
] = s
;
420 if (! ReadInt16LeAsFloat32 (fileName
, fp
, & s
))
422 hData
-> mHrirs
[j1
+ i
] = s
;
430 // Performs the minimum phase reconstruction for a given HRIR data set. The
431 // cepstrum size should be made configureable at some point in the future.
432 static void ReconstructHrirs (int minIrSize
, HrirDataT
* hData
) {
433 int start
, end
, step
, j
;
434 float cep
[CEP_SIZE
];
436 start
= hData
-> mEvOffset
[hData
-> mEvStart
];
437 end
= hData
-> mIrCount
;
438 step
= hData
-> mIrSize
;
439 for (j
= start
; j
< end
; j
++) {
440 RealCepstrum (step
, & hData
-> mHrirs
[j
* step
], cep
);
441 MinimumPhase (cep
, minIrSize
, & hData
-> mHrirs
[j
* minIrSize
]);
443 hData
-> mIrSize
= minIrSize
;
446 // Renormalize the entire HRIR data set, and attenutate it slightly.
447 static void RenormalizeHrirs (const HrirDataT
* hData
) {
448 int step
, start
, end
;
452 step
= hData
-> mIrSize
;
453 start
= hData
-> mEvOffset
[hData
-> mEvStart
] * step
;
454 end
= hData
-> mIrCount
* step
;
456 for (j
= start
; j
< end
; j
+= step
) {
457 for (i
= 0; i
< step
; i
++) {
458 if (fabs (hData
-> mHrirs
[j
+ i
]) > norm
)
459 norm
= fabs (hData
-> mHrirs
[j
+ i
]);
462 if (norm
> 0.000001f
)
465 for (j
= start
; j
< end
; j
+= step
) {
466 for (i
= 0; i
< step
; i
++)
467 hData
-> mHrirs
[j
+ i
] *= norm
;
471 // Given an elevation offset and azimuth, calculates two offsets for
472 // addressing the HRIRs buffer and their interpolation factor.
473 static void CalcAzIndices (const HrirDataT
* hData
, int oi
, float az
, int * j0
, int * j1
, float * jf
) {
476 az
= fmod ((2.0f
* M_PI
) + az
, 2.0f
* M_PI
) * hData
-> mAzCount
[oi
] / (2.0f
* M_PI
);
479 (* j0
) = hData
-> mEvOffset
[oi
] + ai
;
480 (* j1
) = hData
-> mEvOffset
[oi
] + ((ai
+ 1) % hData
-> mAzCount
[oi
]);
484 // Perform a linear interpolation.
485 static float Lerp (float a
, float b
, float f
) {
486 return (a
+ (f
* (b
- a
)));
489 // Attempt to synthesize any missing HRIRs at the bottom elevations. Right
490 // now this just blends the lowest elevation HRIRs together and applies some
491 // attenuates and high frequency damping. It's not a realistic model to use,
493 static void SynthesizeHrirs (HrirDataT
* hData
) {
494 int step
, oi
, i
, a
, j
, e
;
498 float lp
[4], s0
, s1
;
500 if (hData
-> mEvStart
<= 0)
502 step
= hData
-> mIrSize
;
503 oi
= hData
-> mEvStart
;
504 for (i
= 0; i
< step
; i
++)
505 hData
-> mHrirs
[i
] = 0.0f
;
506 for (a
= 0; a
< hData
-> mAzCount
[oi
]; a
++) {
507 j
= (hData
-> mEvOffset
[oi
] + a
) * step
;
508 for (i
= 0; i
< step
; i
++)
509 hData
-> mHrirs
[i
] += hData
-> mHrirs
[j
+ i
] / hData
-> mAzCount
[oi
];
511 for (e
= 1; e
< hData
-> mEvStart
; e
++) {
512 of
= ((float) e
) / hData
-> mEvStart
;
513 for (a
= 0; a
< hData
-> mAzCount
[e
]; a
++) {
514 j
= (hData
-> mEvOffset
[e
] + a
) * step
;
515 CalcAzIndices (hData
, oi
, a
* 2.0f
* M_PI
/ hData
-> mAzCount
[e
], & j0
, & j1
, & jf
);
522 for (i
= 0; i
< step
; i
++) {
523 s0
= hData
-> mHrirs
[i
];
524 s1
= Lerp (hData
-> mHrirs
[j0
+ i
], hData
-> mHrirs
[j1
+ i
], jf
);
525 s0
= Lerp (s0
, s1
, of
);
526 lp
[0] = Lerp (s0
, lp
[0], 0.15f
- (0.15f
* of
));
527 lp
[1] = Lerp (lp
[0], lp
[1], 0.15f
- (0.15f
* of
));
528 lp
[2] = Lerp (lp
[1], lp
[2], 0.15f
- (0.15f
* of
));
529 lp
[3] = Lerp (lp
[2], lp
[3], 0.15f
- (0.15f
* of
));
530 hData
-> mHrirs
[j
+ i
] = lp
[3];
538 for (i
= 0; i
< step
; i
++) {
539 s0
= hData
-> mHrirs
[i
];
540 lp
[0] = Lerp (s0
, lp
[0], 0.15f
);
541 lp
[1] = Lerp (lp
[0], lp
[1], 0.15f
);
542 lp
[2] = Lerp (lp
[1], lp
[2], 0.15f
);
543 lp
[3] = Lerp (lp
[2], lp
[3], 0.15f
);
544 hData
-> mHrirs
[i
] = lp
[3];
546 hData
-> mEvStart
= 0;
549 // Calculate the effective head-related time delays for the each HRIR, now
550 // that they are minimum-phase.
551 static void CalculateHrtds (HrirDataT
* hData
) {
552 float minHrtd
, maxHrtd
;
558 for (e
= 0; e
< hData
-> mEvCount
; e
++) {
559 for (a
= 0; a
< hData
-> mAzCount
[e
]; a
++) {
560 j
= hData
-> mEvOffset
[e
] + a
;
561 t
= CalcLTD ((-90.0f
+ (e
* 180.0f
/ (hData
-> mEvCount
- 1))) * M_PI
/ 180.0f
,
562 (a
* 360.0f
/ hData
-> mAzCount
[e
]) * M_PI
/ 180.0f
,
563 hData
-> mRadius
, hData
-> mDistance
);
564 hData
-> mHrtds
[j
] = t
;
572 for (j
= 0; j
< hData
-> mIrCount
; j
++)
573 hData
-> mHrtds
[j
] -= minHrtd
;
574 hData
-> mMaxHrtd
= maxHrtd
;
577 // Save the OpenAL Soft HRTF data set.
578 static int SaveMhr (const HrirDataT
* hData
, const char * fileName
) {
580 int e
, step
, end
, j
, i
;
582 if ((fp
= fopen (fileName
, "wb")) == NULL
) {
583 fprintf (stderr
, "Could not create file, '%s'.\n", fileName
);
586 if (! WriteString (MHR_FORMAT
, fileName
, fp
))
588 if (! WriteUInt32Le ((ALuint
) hData
-> mIrRate
, fileName
, fp
))
590 if (! WriteUInt16Le ((ALushort
) hData
-> mIrCount
, fileName
, fp
))
592 if (! WriteUInt16Le ((ALushort
) hData
-> mIrSize
, fileName
, fp
))
594 if (! WriteUInt8 ((ALubyte
) hData
-> mEvCount
, fileName
, fp
))
596 for (e
= 0; e
< hData
-> mEvCount
; e
++) {
597 if (! WriteUInt16Le ((ALushort
) hData
-> mEvOffset
[e
], fileName
, fp
))
600 step
= hData
-> mIrSize
;
601 end
= hData
-> mIrCount
* step
;
602 for (j
= 0; j
< end
; j
+= step
) {
603 for (i
= 0; i
< step
; i
++) {
604 if (! WriteFloat32AsInt16Le (hData
-> mHrirs
[j
+ i
], fileName
, fp
))
608 for (j
= 0; j
< hData
-> mIrCount
; j
++) {
609 i
= (int) round (44100.0f
* hData
-> mHrtds
[j
]);
612 if (! WriteUInt8 ((ALubyte
) i
, fileName
, fp
))
619 // Save the OpenAL Soft built-in table.
620 static int SaveTab (const HrirDataT
* hData
, const char * fileName
) {
625 if ((fp
= fopen (fileName
, "wb")) == NULL
) {
626 fprintf (stderr
, "Could not create file, '%s'.\n", fileName
);
629 if (! WriteString ("/* This data is Copyright 1994 by the MIT Media Laboratory. It is provided free\n"
630 " * with no restrictions on use, provided the authors are cited when the data is\n"
631 " * used in any research or commercial application. */\n"
632 "/* Bill Gardner <billg@media.mit.edu> and Keith Martin <kdm@media.mit.edu> */\n"
634 " /* HRIR Coefficients */\n"
635 " {\n", fileName
, fp
))
637 step
= hData
-> mIrSize
;
638 end
= hData
-> mIrCount
* step
;
639 for (j
= 0; j
< end
; j
+= step
) {
640 if (! WriteString (" { ", fileName
, fp
))
642 for (i
= 0; i
< step
; i
++) {
643 snprintf (text
, 15, "%+d, ", (int) round (32767.0f
* hData
-> mHrirs
[j
+ i
]));
644 if (! WriteString (text
, fileName
, fp
))
647 if (! WriteString ("},\n", fileName
, fp
))
650 if (! WriteString (" },\n"
652 " /* HRIR Delays */\n"
653 " { ", fileName
, fp
))
655 for (j
= 0; j
< hData
-> mIrCount
; j
++) {
656 snprintf (text
, 15, "%d, ", (int) round (44100.0f
* hData
-> mHrtds
[j
]));
657 if (! WriteString (text
, fileName
, fp
))
660 if (! WriteString ("}\n", fileName
, fp
))
666 // Loads and processes an MIT data set. At present, the HRIR and HRTD data
667 // is loaded and processed in a static buffer. That should change to using
668 // heap allocated memory in the future. A cleanup function will then be
670 static int MakeMit(const char *baseInName
, HrirDataT
*hData
)
672 static float hrirs
[MIT_IR_COUNT
* MIT_IR_SIZE
];
673 static float hrtds
[MIT_IR_COUNT
];
675 hData
->mIrRate
= MIT_IR_RATE
;
676 hData
->mIrCount
= MIT_IR_COUNT
;
677 hData
->mIrSize
= MIT_IR_SIZE
;
678 hData
->mEvCount
= MIT_EV_COUNT
;
679 hData
->mEvStart
= MIT_EV_START
;
680 hData
->mEvOffset
= MIT_EV_OFFSET
;
681 hData
->mAzCount
= MIT_AZ_COUNT
;
682 hData
->mRadius
= MIT_RADIUS
;
683 hData
->mDistance
= MIT_DISTANCE
;
684 hData
->mHrirs
= hrirs
;
685 hData
->mHrtds
= hrtds
;
686 fprintf(stderr
, "Loading base HRIR data...\n");
687 if(!LoadMitHrirs(baseInName
, hData
))
689 fprintf(stderr
, "Performing minimum phase reconstruction and truncation...\n");
690 ReconstructHrirs(MIN_IR_SIZE
, hData
);
691 fprintf(stderr
, "Renormalizing minimum phase HRIR data...\n");
692 RenormalizeHrirs(hData
);
693 fprintf(stderr
, "Synthesizing missing elevations...\n");
694 SynthesizeHrirs(hData
);
695 fprintf(stderr
, "Calculating impulse delays...\n");
696 CalculateHrtds(hData
);
700 // Simple dispatch. Provided a command, the path to the MIT set of choice,
701 // and an optional output filename, this will produce an OpenAL Soft
702 // compatible HRTF set in the chosen format.
703 int main(int argc
, char *argv
[])
706 const char *outName
= NULL
;
709 if(argc
< 3 || strcmp(argv
[1], "-h") == 0 || strcmp (argv
[1], "--help") == 0)
711 fprintf(stderr
, "Usage: %s <command> <path of MIT set> [ <output file> ]\n\n", argv
[0]);
712 fprintf(stderr
, "Commands:\n");
713 fprintf(stderr
, " -m, --make-mhr Makes an OpenAL Soft compatible HRTF data set.\n");
714 fprintf(stderr
, " Defaults output to: ./oal_soft_hrtf_44100.mhr\n");
715 fprintf(stderr
, " -t, --make-tab Makes the built-in table used when compiling OpenAL Soft.\n");
716 fprintf(stderr
, " Defaults output to: ./hrtf_tables.inc\n");
717 fprintf(stderr
, " -h, --help Displays this help information.\n");
721 snprintf(baseName
, sizeof(baseName
), "%s/elev", argv
[2]);
722 if(strcmp(argv
[1], "-m") == 0 || strcmp(argv
[1], "--make-mhr") == 0)
727 outName
= "./oal_soft_hrtf_44100.mhr";
728 if(!MakeMit(baseName
, &hData
))
730 fprintf(stderr
, "Creating data set file...\n");
731 if(!SaveMhr(&hData
, outName
))
734 else if(strcmp(argv
[1], "-t") == 0 || strcmp(argv
[1], "--make-tab") == 0)
739 outName
= "./hrtf_tables.inc";
740 if(!MakeMit(baseName
, &hData
))
742 fprintf(stderr
, "Creating table file...\n");
743 if(!SaveTab(&hData
, outName
))
748 fprintf(stderr
, "Invalid command '%s'\n", argv
[1]);
751 fprintf(stderr
, "Done.\n");