Use an 8-channel cube for HRTF's virtual format.
There were phase issues caused by applying HRTF directly to the B-Format channels, since the HRIR delays were all averaged which removed the inter-aural time-delay, which in turn removed significant spatial information.
This commit is contained in:
@@ -100,14 +100,14 @@ extern inline void aluMatrixdSet(aluMatrixd *matrix,
|
||||
ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33);
|
||||
|
||||
|
||||
/* NOTE: HRTF is set up a bit special in the device. By default, the device's
|
||||
* DryBuffer, NumChannels, ChannelName, and Channel fields correspond to the
|
||||
* output mixing format, and the DryBuffer is then converted and written to the
|
||||
* backend's audio buffer.
|
||||
/* NOTE: HRTF is set up a bit special in the device. By default, without HRTF,
|
||||
* the device's DryBuffer, NumChannels, ChannelName, and Channel fields
|
||||
* correspond to the output format, and the DryBuffer is then converted and
|
||||
* written to the backend's audio buffer.
|
||||
*
|
||||
* With HRTF, these fields correspond to a virtual format (typically B-Format),
|
||||
* and the actual output is stored in DryBuffer[NumChannels] for the left
|
||||
* channel and DryBuffer[NumChannels+1] for the right. As a final output step,
|
||||
* With HRTF, these fields correspond to a virtual format, and the actual
|
||||
* output is stored in DryBuffer[NumChannels] for the left channel and
|
||||
* DryBuffer[NumChannels+1] for the right. As a final output step,
|
||||
* the virtual channels will have HRTF applied and written to the actual
|
||||
* output. Things like effects and B-Format decoding will want to write to the
|
||||
* virtual channels so that they can be mixed with HRTF in full 3D.
|
||||
|
||||
-102
@@ -181,108 +181,6 @@ void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azi
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates HRTF coefficients for B-Format channels (only up to first-order).
|
||||
* Note that these will decode a B-Format output mix, which uses FuMa ordering
|
||||
* and scaling, not N3D!
|
||||
*/
|
||||
void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat (*coeffs_list[4])[2], ALuint *delay_list[4])
|
||||
{
|
||||
ALuint elev_idx, azi_idx;
|
||||
ALfloat scale;
|
||||
ALuint i, c;
|
||||
|
||||
for(c = 0;c < 4;c++)
|
||||
{
|
||||
ALfloat (*coeffs)[2] = coeffs_list[c];
|
||||
ALuint *delay = delay_list[c];
|
||||
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
}
|
||||
delay[0] = 0;
|
||||
delay[1] = 0;
|
||||
}
|
||||
|
||||
/* NOTE: HRTF coefficients are generated by combining all the HRIRs in the
|
||||
* dataset, with each entry scaled according to how much it contributes to
|
||||
* the given B-Format channel based on its direction (including negative
|
||||
* contributions!).
|
||||
*/
|
||||
scale = 0.0f;
|
||||
for(elev_idx = 0;elev_idx < Hrtf->evCount;elev_idx++)
|
||||
{
|
||||
ALfloat elev = (ALfloat)elev_idx/(ALfloat)(Hrtf->evCount-1)*F_PI - F_PI_2;
|
||||
ALuint evoffset = Hrtf->evOffset[elev_idx];
|
||||
ALuint azcount = Hrtf->azCount[elev_idx];
|
||||
|
||||
scale += (ALfloat)azcount;
|
||||
|
||||
for(azi_idx = 0;azi_idx < azcount;azi_idx++)
|
||||
{
|
||||
ALuint lidx, ridx;
|
||||
ALfloat ambi_coeffs[4];
|
||||
ALfloat az, gain;
|
||||
ALfloat x, y, z;
|
||||
|
||||
lidx = evoffset + azi_idx;
|
||||
ridx = evoffset + ((azcount-azi_idx) % azcount);
|
||||
|
||||
az = (ALfloat)azi_idx / (ALfloat)azcount * F_TAU;
|
||||
if(az > F_PI) az -= F_TAU;
|
||||
|
||||
x = cosf(-az) * cosf(elev);
|
||||
y = sinf(-az) * cosf(elev);
|
||||
z = sinf(elev);
|
||||
|
||||
ambi_coeffs[0] = 1.414213562f;
|
||||
ambi_coeffs[1] = x;
|
||||
ambi_coeffs[2] = y;
|
||||
ambi_coeffs[3] = z;
|
||||
|
||||
for(c = 0;c < 4;c++)
|
||||
{
|
||||
ALfloat (*coeffs)[2] = coeffs_list[c];
|
||||
ALuint *delay = delay_list[c];
|
||||
|
||||
/* NOTE: Always include the total delay average since the
|
||||
* channels need to have matching delays. */
|
||||
delay[0] += Hrtf->delays[lidx];
|
||||
delay[1] += Hrtf->delays[ridx];
|
||||
|
||||
gain = ambi_coeffs[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] += Hrtf->coeffs[lidx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
|
||||
coeffs[i][1] += Hrtf->coeffs[ridx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scale = 1.0f/scale;
|
||||
|
||||
for(c = 0;c < 4;c++)
|
||||
{
|
||||
ALfloat (*coeffs)[2] = coeffs_list[c];
|
||||
ALuint *delay = delay_list[c];
|
||||
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] *= scale;
|
||||
coeffs[i][1] *= scale;
|
||||
}
|
||||
delay[0] = minu((ALuint)((ALfloat)delay[0] * scale), HRTF_HISTORY_LENGTH-1);
|
||||
delay[0] <<= HRTFDELAY_BITS;
|
||||
delay[1] = minu((ALuint)((ALfloat)delay[1] * scale), HRTF_HISTORY_LENGTH-1);
|
||||
delay[1] <<= HRTFDELAY_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf00(FILE *f)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,5 @@ ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf);
|
||||
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
|
||||
|
||||
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
|
||||
void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat (*coeffs_list[4])[2], ALuint *delay_list[4]);
|
||||
|
||||
#endif /* ALC_HRTF_H */
|
||||
|
||||
+39
-12
@@ -184,6 +184,15 @@ DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
|
||||
case SideLeft: return "side-left";
|
||||
case SideRight: return "side-right";
|
||||
|
||||
case UpperFrontLeft: return "upper-front-left";
|
||||
case UpperFrontRight: return "upper-front-right";
|
||||
case UpperBackLeft: return "upper-back-left";
|
||||
case UpperBackRight: return "upper-back-right";
|
||||
case LowerFrontLeft: return "lower-front-left";
|
||||
case LowerFrontRight: return "lower-front-right";
|
||||
case LowerBackLeft: return "lower-back-left";
|
||||
case LowerBackRight: return "lower-back-right";
|
||||
|
||||
case BFormatW: return "bformat-w";
|
||||
case BFormatX: return "bformat-x";
|
||||
case BFormatY: return "bformat-y";
|
||||
@@ -466,6 +475,15 @@ ALvoid aluInitPanning(ALCdevice *device)
|
||||
{ BackRight, { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f } },
|
||||
{ SideLeft, { 0.224739f, 0.000000f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f } },
|
||||
{ SideRight, { 0.224739f, 0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f } },
|
||||
}, Cube8Cfg[8] = {
|
||||
{ UpperFrontLeft, { 0.176776695f, 0.072168784f, 0.072168784f, 0.072168784f } },
|
||||
{ UpperFrontRight, { 0.176776695f, 0.072168784f, -0.072168784f, 0.072168784f } },
|
||||
{ UpperBackLeft, { 0.176776695f, -0.072168784f, 0.072168784f, 0.072168784f } },
|
||||
{ UpperBackRight, { 0.176776695f, -0.072168784f, -0.072168784f, 0.072168784f } },
|
||||
{ LowerFrontLeft, { 0.176776695f, 0.072168784f, 0.072168784f, -0.072168784f } },
|
||||
{ LowerFrontRight, { 0.176776695f, 0.072168784f, -0.072168784f, -0.072168784f } },
|
||||
{ LowerBackLeft, { 0.176776695f, -0.072168784f, 0.072168784f, -0.072168784f } },
|
||||
{ LowerBackRight, { 0.176776695f, -0.072168784f, -0.072168784f, -0.072168784f } },
|
||||
}, BFormat3D[4] = {
|
||||
{ BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
|
||||
{ BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } },
|
||||
@@ -482,13 +500,25 @@ ALvoid aluInitPanning(ALCdevice *device)
|
||||
|
||||
if(device->Hrtf)
|
||||
{
|
||||
ALfloat (*coeffs_list[4])[2];
|
||||
ALuint *delay_list[4];
|
||||
static const struct {
|
||||
enum Channel Channel;
|
||||
ALfloat Angle;
|
||||
ALfloat Elevation;
|
||||
} CubeInfo[8] = {
|
||||
{ UpperFrontLeft, DEG2RAD( -45.0f), DEG2RAD( 45.0f) },
|
||||
{ UpperFrontRight, DEG2RAD( 45.0f), DEG2RAD( 45.0f) },
|
||||
{ UpperBackLeft, DEG2RAD(-135.0f), DEG2RAD( 45.0f) },
|
||||
{ UpperBackRight, DEG2RAD( 135.0f), DEG2RAD( 45.0f) },
|
||||
{ LowerFrontLeft, DEG2RAD( -45.0f), DEG2RAD(-45.0f) },
|
||||
{ LowerFrontRight, DEG2RAD( 45.0f), DEG2RAD(-45.0f) },
|
||||
{ LowerBackLeft, DEG2RAD(-135.0f), DEG2RAD(-45.0f) },
|
||||
{ LowerBackRight, DEG2RAD( 135.0f), DEG2RAD(-45.0f) },
|
||||
};
|
||||
ALuint i;
|
||||
|
||||
count = COUNTOF(BFormat3D);
|
||||
chanmap = BFormat3D;
|
||||
ambiscale = 1.0f;
|
||||
count = COUNTOF(Cube8Cfg);
|
||||
chanmap = Cube8Cfg;
|
||||
ambiscale = FIRST_ORDER_SCALE;
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
device->ChannelName[i] = chanmap[i].ChanName;
|
||||
@@ -498,15 +528,12 @@ ALvoid aluInitPanning(ALCdevice *device)
|
||||
&device->NumChannels, AL_TRUE);
|
||||
device->AmbiScale = ambiscale;
|
||||
|
||||
for(i = 0;i < 4;++i)
|
||||
for(i = 0;i < device->NumChannels;i++)
|
||||
{
|
||||
static const enum Channel inputs[4] = { BFormatW, BFormatX, BFormatY, BFormatZ };
|
||||
int chan = GetChannelIdxByName(device, inputs[i]);
|
||||
coeffs_list[i] = device->Hrtf_Params[chan].Coeffs;
|
||||
delay_list[i] = device->Hrtf_Params[chan].Delay;
|
||||
int chan = GetChannelIdxByName(device, CubeInfo[i].Channel);
|
||||
GetLerpedHrtfCoeffs(device->Hrtf, CubeInfo[i].Elevation, CubeInfo[i].Angle, 1.0f, 1.0f,
|
||||
device->Hrtf_Params[chan].Coeffs, device->Hrtf_Params[chan].Delay);
|
||||
}
|
||||
GetBFormatHrtfCoeffs(device->Hrtf, coeffs_list, delay_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -317,6 +317,15 @@ enum Channel {
|
||||
SideLeft,
|
||||
SideRight,
|
||||
|
||||
UpperFrontLeft,
|
||||
UpperFrontRight,
|
||||
UpperBackLeft,
|
||||
UpperBackRight,
|
||||
LowerFrontLeft,
|
||||
LowerFrontRight,
|
||||
LowerBackLeft,
|
||||
LowerBackRight,
|
||||
|
||||
BFormatW,
|
||||
BFormatX,
|
||||
BFormatY,
|
||||
|
||||
Reference in New Issue
Block a user