diff options
author | Chris Robinson <[email protected]> | 2011-07-04 07:14:45 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2011-07-04 07:20:38 -0700 |
commit | d307ee8c2f0823f80b427766ace50243db0999d5 (patch) | |
tree | 7ffadef9d33578ebe3e5c0656d2aeac05de143cc | |
parent | 202f57552ad353912c54522605531e9737c790b0 (diff) |
Implement a lerped lookup of the HRTF coefficients/delays
Code supplied by Christopher Fitzgerald
-rw-r--r-- | Alc/ALu.c | 35 | ||||
-rw-r--r-- | Alc/hrtf.c | 102 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 2 |
3 files changed, 93 insertions, 46 deletions
@@ -269,8 +269,6 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) { for(c = 0;c < num_channels;c++) { - const ALshort *hrtf_left, *hrtf_right; - if(chans[c] == LFE) { /* Skip LFE */ @@ -284,17 +282,10 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) continue; } - GetHrtfCoeffs(0.0, angles[c] * (M_PI/180.0), - &hrtf_left, &hrtf_right, - &ALSource->Params.HrtfDelay[c][0], - &ALSource->Params.HrtfDelay[c][1]); - for(i = 0;i < HRIR_LENGTH;i++) - { - ALSource->Params.HrtfCoeffs[c][i][0] = - hrtf_left[i]*(1.0/32767.0)*DryGain*ListenerGain; - ALSource->Params.HrtfCoeffs[c][i][1] = - hrtf_right[i]*(1.0/32767.0)*DryGain*ListenerGain; - } + GetLerpedHrtfCoeffs(0.0, angles[c] * (M_PI/180.0), + DryGain*ListenerGain, + ALSource->Params.HrtfCoeffs[c], + ALSource->Params.HrtfDelay[c]); } } else @@ -692,11 +683,9 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) BufferListItem = BufferListItem->next; } - // Use energy-preserving panning algorithm for multi-speaker playback if((Device->Flags&DEVICE_USE_HRTF)) { - const ALshort *hrtf_left, *hrtf_right; - + // Use a binaural HRTF algorithm for stereo headphone playback if(Distance > 0.0f) { ALfloat invlen = 1.0f/Distance; @@ -705,18 +694,14 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) Position[2] *= invlen; } - GetHrtfCoeffs(asin(Position[1]), atan2(Position[0], -Position[2]*ZScale), - &hrtf_left, &hrtf_right, - &ALSource->Params.HrtfDelay[0][0], - &ALSource->Params.HrtfDelay[0][1]); - for(i = 0;i < HRIR_LENGTH;i++) - { - ALSource->Params.HrtfCoeffs[0][i][0] = hrtf_left[i]*(1.0/32767.0) * DryGain; - ALSource->Params.HrtfCoeffs[0][i][1] = hrtf_right[i]*(1.0/32767.0) * DryGain; - } + GetLerpedHrtfCoeffs(asin(Position[1]), + atan2(Position[0], -Position[2]*ZScale), DryGain, + ALSource->Params.HrtfCoeffs[0], + ALSource->Params.HrtfDelay[0]); } else { + // Use energy-preserving panning algorithm for multi-speaker playback ALfloat DirGain, AmbientGain; const ALfloat *SpeakerGain; ALfloat length; @@ -39,34 +39,96 @@ static struct HRTF { #include "hrtf_tables.inc" }; -static ALuint CalcEvIndex(ALdouble ev) +// Calculate the elevation indices given the polar elevation in radians. +// This will return two indices between 0 and (evCount - 1) and an +// interpolation factor between 0.0 and 1.0. +static void CalcEvIndices (ALfloat ev, ALuint evidx [2], ALfloat * evmu) { - ev = (M_PI/2.0 + ev) * (evCount-1) / M_PI; - return (ALuint)(ev+0.5); + ev = (M_PI/2.0f + ev) * (evCount-1) / M_PI; + evidx[0] = (ALuint)ev; + evidx[1] = __min(evidx[0] + 1, evCount - 1); + *evmu = ev - evidx[0]; } -static ALuint CalcAzIndex(ALint evidx, ALdouble az) +// Calculate the azimuth indices given the polar azimuth in radians. This +// will return two indices between 0 and (azCount [ei] - 1) and an +// interpolation factor between 0.0 and 1.0. +static void CalcAzIndices (ALuint evidx, ALfloat az, ALuint azidx [2], ALfloat * azmu) { - az = (M_PI*2.0 + az) * azCount[evidx] / (M_PI*2.0); - return (ALuint)(az+0.5) % azCount[evidx]; + az = (M_PI*2.0f + az) * azCount[evidx] / (M_PI*2.0f); + azidx[0] = (ALuint)az % azCount[evidx]; + azidx[1] = (azidx[0] + 1) % azCount[evidx]; + *azmu = az - (ALuint)az; } -void GetHrtfCoeffs(ALfloat elevation, ALfloat angle, const ALshort **left, const ALshort **right, ALuint *ldelay, ALuint *rdelay) +// Calculates static HRIR coefficients and delays for the given polar +// elevation and azimuth in radians. Linear interpolation is used to +// increase the apparent resolution of the HRIR dataset. The coefficients +// are also normalized and attenuated by the specified gain. +void GetLerpedHrtfCoeffs(ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays) { - ALuint lidx, ridx; - ALuint evidx, azidx; - - evidx = CalcEvIndex(elevation); - azidx = CalcAzIndex(evidx, angle); - - lidx = evOffset[evidx] + azidx; - ridx = evOffset[evidx] + ((azCount[evidx]-azidx) % azCount[evidx]); - - *ldelay = Hrtf.delays[lidx]; - *rdelay = Hrtf.delays[ridx]; + ALuint evidx[2], azidx[2]; + ALfloat mu[3]; + ALuint lidx[4], ridx[4]; + ALuint i; + + // Claculate elevation indices and interpolation factor. + CalcEvIndices(elevation, evidx, &mu[2]); + + // Calculate azimuth indices and interpolation factor for the first + // elevation. + CalcAzIndices(evidx[0], azimuth, azidx, &mu[0]); + + // Calculate the first set of linear HRIR indices for left and right + // channels. + lidx[0] = evOffset[evidx[0]] + azidx[0]; + lidx[1] = evOffset[evidx[0]] + azidx[1]; + ridx[0] = evOffset[evidx[0]] + ((azCount[evidx[0]]-azidx[0]) % azCount[evidx[0]]); + ridx[1] = evOffset[evidx[0]] + ((azCount[evidx[0]]-azidx[1]) % azCount[evidx[0]]); + + // Calculate azimuth indices and interpolation factor for the second + // elevation. + CalcAzIndices (evidx[1], azimuth, azidx, &mu[1]); + + // Calculate the second set of linear HRIR indices for left and right + // channels. + lidx[2] = evOffset[evidx[1]] + azidx[0]; + lidx[3] = evOffset[evidx[1]] + azidx[1]; + ridx[2] = evOffset[evidx[1]] + ((azCount[evidx[1]]-azidx[0]) % azCount[evidx[1]]); + ridx[3] = evOffset[evidx[1]] + ((azCount[evidx[1]]-azidx[1]) % azCount[evidx[1]]); + + // Calculate the normalized and attenuated HRIR coefficients using linear + // interpolation when there is enough gain to warrant it. Zero the + // coefficients if gain is too low. + if(gain > 0.0001f) + { + ALdouble scale = gain * (1.0/32767.0); + for(i = 0;i < HRIR_LENGTH;i++) + { + coeffs[i][0] = lerp(lerp(Hrtf.coeffs[lidx[0]][i], Hrtf.coeffs[lidx[1]][i], mu[0]), + lerp(Hrtf.coeffs[lidx[2]][i], Hrtf.coeffs[lidx[3]][i], mu[1]), + mu[2]) * scale; + coeffs[i][1] = lerp(lerp(Hrtf.coeffs[ridx[0]][i], Hrtf.coeffs[ridx[1]][i], mu[0]), + lerp(Hrtf.coeffs[ridx[2]][i], Hrtf.coeffs[ridx[3]][i], mu[1]), + mu[2]) * scale; + } + } + else + { + for(i = 0;i < HRIR_LENGTH;i++) + { + coeffs[i][0] = 0.0f; + coeffs[i][1] = 0.0f; + } + } - *left = Hrtf.coeffs[lidx]; - *right = Hrtf.coeffs[ridx]; + // Calculate the HRIR delays using linear interpolation. + delays[0] = (ALuint)(lerp(lerp(Hrtf.delays[lidx[0]], Hrtf.delays[lidx[1]], mu[0]), + lerp(Hrtf.delays[lidx[2]], Hrtf.delays[lidx[3]], mu[1]), + mu[2]) + 0.5f); + delays[1] = (ALuint)(lerp(lerp(Hrtf.delays[ridx[0]], Hrtf.delays[ridx[1]], mu[0]), + lerp(Hrtf.delays[ridx[2]], Hrtf.delays[ridx[3]], mu[1]), + mu[2]) + 0.5f); } ALCboolean IsHrtfCompatible(ALCdevice *device) diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 26a3bf30..1fb75e18 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -523,7 +523,7 @@ const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans); #define HRIR_MASK (HRIR_LENGTH-1) void InitHrtf(void); ALCboolean IsHrtfCompatible(ALCdevice *device); -void GetHrtfCoeffs(ALfloat elevation, ALfloat angle, const ALshort **left, const ALshort **right, ALuint *ldelay, ALuint *rdelay); +void GetLerpedHrtfCoeffs(ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays); void al_print(const char *fname, unsigned int line, const char *fmt, ...) PRINTF_STYLE(3,4); |