Compensate the near-field azimuth correction for the parallax baked into the HRTF dataset

This commit is contained in:
Ken Cooke 2018-02-27 12:49:32 -08:00
parent cc7b9a254b
commit 41ccf6afd8
2 changed files with 26 additions and 12 deletions

View file

@ -775,20 +775,28 @@ static void distanceBiquad(float distance, float& b0, float& b1, float& b2, floa
// Geometric correction of the azimuth, to the angle at each ear. // Geometric correction of the azimuth, to the angle at each ear.
// D. Brungart, "Auditory parallax effects in the HRTF for nearby sources," IEEE WASPAA (1999). // D. Brungart, "Auditory parallax effects in the HRTF for nearby sources," IEEE WASPAA (1999).
// //
static void nearFieldAzimuth(float azimuth, float distance, float& azimuthL, float& azimuthR) { static void nearFieldAzimuthCorrection(float azimuth, float distance, float& azimuthL, float& azimuthR) {
// FIXME: optimize
float dx = distance * cosf(azimuth); float dx = distance * cosf(azimuth);
float dy = distance * sinf(azimuth); float dy = distance * sinf(azimuth);
azimuthL = atan2f(dy + HRTF_HEAD_RADIUS, dx); // at reference distance, the azimuth parallax is correct
azimuthR = atan2f(dy - HRTF_HEAD_RADIUS, dx); float dx0 = HRTF_AZIMUTH_REF * cosf(azimuth);
float dy0 = HRTF_AZIMUTH_REF * sinf(azimuth);
float deltaL = atan2f(dy + HRTF_HEAD_RADIUS, dx) - atan2f(dy0 + HRTF_HEAD_RADIUS, dx0);
float deltaR = atan2f(dy - HRTF_HEAD_RADIUS, dx) - atan2f(dy0 - HRTF_HEAD_RADIUS, dx0);
// add the azimuth correction
// NOTE: must converge to 0 when distance = HRTF_AZIMUTH_REF
azimuthL += deltaL;
azimuthR += deltaR;
} }
// //
// Approximate the near-field DC gain correction at each ear. // Approximate the near-field DC gain correction at each ear.
// //
static void nearFieldGain(float azimuth, float distance, float& gainL, float& gainR) { static void nearFieldGainCorrection(float azimuth, float distance, float& gainL, float& gainR) {
// normalized distance factor = [0,1] as distance = [HRTF_NEARFIELD_MAX,HRTF_HEAD_RADIUS] // normalized distance factor = [0,1] as distance = [HRTF_NEARFIELD_MAX,HRTF_HEAD_RADIUS]
assert(distance < HRTF_NEARFIELD_MAX); assert(distance < HRTF_NEARFIELD_MAX);
@ -816,9 +824,9 @@ static void nearFieldGain(float azimuth, float distance, float& gainL, float& ga
float cR = ((-0.000452339132f * angleR - 0.00173192444f) * angleR + 0.162476536f) * angleR; float cR = ((-0.000452339132f * angleR - 0.00173192444f) * angleR + 0.162476536f) * angleR;
// approximate the gain correction // approximate the gain correction
// NOTE: this must converge to 1.0 when distance = HRTF_NEARFIELD_MAX at all azimuth // NOTE: must converge to 0 when distance = HRTF_NEARFIELD_MAX
gainL = 1.0f - d * cL; gainL -= d * cL;
gainR = 1.0f - d * cR; gainR -= d * cR;
} }
// //
@ -894,14 +902,17 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela
distance = MAX(distance, HRTF_NEARFIELD_MIN); distance = MAX(distance, HRTF_NEARFIELD_MIN);
// compute the azimuth correction at each ear // compute the azimuth correction at each ear
float azimuthL, azimuthR; float azimuthL = azimuth;
nearFieldAzimuth(azimuth, distance, azimuthL, azimuthR); float azimuthR = azimuth;
if (distance < HRTF_AZIMUTH_REF) {
nearFieldAzimuthCorrection(azimuth, distance, azimuthL, azimuthR);
}
// compute the DC gain correction at each ear // compute the DC gain correction at each ear
float gainL = 1.0f; float gainL = 1.0f;
float gainR = 1.0f; float gainR = 1.0f;
if (distance < HRTF_NEARFIELD_MAX) { if (distance < HRTF_NEARFIELD_MAX) {
nearFieldGain(azimuth, distance, gainL, gainR); nearFieldGainCorrection(azimuth, distance, gainL, gainR);
} }
// parameters for table interpolation // parameters for table interpolation
@ -976,7 +987,8 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela
// //
// Second biquad implements the near-field or distance filter. // Second biquad implements the near-field or distance filter.
// //
if (distance < 1.0f) { if (distance < HRTF_NEARFIELD_MAX) {
nearFieldFilter(gainL, b0, b1, a1); nearFieldFilter(gainL, b0, b1, a1);
bqCoef[0][channel+4] = b0; bqCoef[0][channel+4] = b0;
@ -994,6 +1006,7 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela
bqCoef[4][channel+5] = 0.0f; bqCoef[4][channel+5] = 0.0f;
} else { } else {
distanceBiquad(distance, b0, b1, b2, a1, a2); distanceBiquad(distance, b0, b1, b2, a1, a2);
bqCoef[0][channel+4] = b0; bqCoef[0][channel+4] = b0;

View file

@ -24,6 +24,7 @@ static const int HRTF_BLOCK = 240; // block processing size
static const float HRTF_GAIN = 1.0f; // HRTF global gain adjustment static const float HRTF_GAIN = 1.0f; // HRTF global gain adjustment
// Near-field HRTF // Near-field HRTF
static const float HRTF_AZIMUTH_REF = 2.0f; // IRCAM Listen HRTF was recorded at 2 meters
static const float HRTF_NEARFIELD_MAX = 1.0f; // distance in meters static const float HRTF_NEARFIELD_MAX = 1.0f; // distance in meters
static const float HRTF_NEARFIELD_MIN = 0.125f; // distance in meters static const float HRTF_NEARFIELD_MIN = 0.125f; // distance in meters
static const float HRTF_HEAD_RADIUS = 0.0875f; // average human head in meters static const float HRTF_HEAD_RADIUS = 0.0875f; // average human head in meters