diff options
author | Chris Robinson <[email protected]> | 2017-01-15 13:57:22 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2017-01-15 13:57:22 -0800 |
commit | 9f23d17333c8faaa0a2b7a86df33c41874a929a5 (patch) | |
tree | dd68191ae059b295ada025738d3185845596bdc0 | |
parent | 8e868823fd0226a960259363cd7b49ea51ece426 (diff) |
Use second-order ambisonics for basic HRTF rendering
This should improve positional quality for relatively low cost. Full HRTF
rendering still only uses first-order since the only use of the dry buffer
there is for first-order content (B-Format buffers, effects).
-rw-r--r-- | Alc/ALc.c | 11 | ||||
-rw-r--r-- | Alc/ALu.c | 7 | ||||
-rw-r--r-- | Alc/hrtf.c | 39 | ||||
-rw-r--r-- | Alc/hrtf.h | 2 | ||||
-rw-r--r-- | Alc/panning.c | 57 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 4 |
6 files changed, 95 insertions, 25 deletions
@@ -2055,9 +2055,13 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2)) size += (ChannelsFromDevFmt(device->FmtChans)+4) * sizeof(device->Dry.Buffer[0]); else if(device->Hrtf.Handle || device->Uhj_Encoder || device->AmbiDecoder) + { size += ChannelsFromDevFmt(device->FmtChans) * sizeof(device->Dry.Buffer[0]); - else if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3) + if(device->AmbiUp) size += 4 * sizeof(device->Dry.Buffer[0]); + } + else if(device->AmbiUp) size += 4 * sizeof(device->Dry.Buffer[0]); + TRACE("Allocating "SZFMT" channels, "SZFMT" bytes\n", size/sizeof(device->Dry.Buffer[0]), size); device->Dry.Buffer = al_calloc(16, size); if(!device->Dry.Buffer) { @@ -2076,8 +2080,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) device->RealOut.NumChannels = device->Dry.NumChannels; } - if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2) || - (device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)) + if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2) || device->AmbiUp) { /* Higher-order rendering requires upsampling first-order content, so * make sure to mix it separately. @@ -2090,6 +2093,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) device->FOAOut.Buffer = device->Dry.Buffer; device->FOAOut.NumChannels = device->Dry.NumChannels; } + TRACE("Channel config, Dry: %u, FOA: %u, Real: %u\n", device->Dry.NumChannels, + device->FOAOut.NumChannels, device->RealOut.NumChannels); SetMixerFPUMode(&oldMode); if(device->DefaultSlot) @@ -1488,6 +1488,13 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) { int lidx = GetChannelIdxByName(device->RealOut, FrontLeft); int ridx = GetChannelIdxByName(device->RealOut, FrontRight); + + if(device->AmbiUp) + ambiup_process(device->AmbiUp, + device->Dry.Buffer, device->Dry.NumChannels, + SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo + ); + if(lidx != -1 && ridx != -1) { HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer(); @@ -136,12 +136,14 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, } -ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels) +ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels, const ALuint *AmbiMap) { +#define HRTF_AMBI_CHAN_COUNT 14 + /* NOTE: azimuth goes clockwise. */ static const struct { ALfloat elevation; ALfloat azimuth; - } Ambi3DPoints[14] = { + } Ambi3DPoints[HRTF_AMBI_CHAN_COUNT] = { { DEG2RAD( 90.0f), DEG2RAD( 0.0f) }, { DEG2RAD( 35.0f), DEG2RAD( -45.0f) }, { DEG2RAD( 35.0f), DEG2RAD( 45.0f) }, @@ -157,7 +159,7 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ { DEG2RAD(-35.0f), DEG2RAD(-135.0f) }, { DEG2RAD(-90.0f), DEG2RAD( 0.0f) }, }; - static const ALfloat Ambi3DMatrix[14][2][MAX_AMBI_COEFFS] = { + static const ALfloat Ambi3DMatrixFOA[HRTF_AMBI_CHAN_COUNT][2][MAX_AMBI_COEFFS] = { { { 1.88982237e-001f, 0.00000000e+000f, 1.90399923e-001f, 0.00000000e+000f }, { 7.14285714e-002f, 0.00000000e+000f, 1.24646009e-001f, 0.00000000e+000f } }, { { 1.88982237e-001f, 1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f } }, { { 1.88982237e-001f, -1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f } }, @@ -172,9 +174,25 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ { { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } }, { { 1.88982237e-001f, 1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } }, { { 1.88982237e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f }, { 7.14285714e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f } } + }, Ambi3DMatrixHOA[HRTF_AMBI_CHAN_COUNT][2][MAX_AMBI_COEFFS] = { + { { 1.43315266e-001f, 0.00000000e+000f, 1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, 1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } }, + { { 1.40852210e-001f, 1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f, 7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f, 9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f, 7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f, 9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, 1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.39644596e-001f, 0.00000000e+000f, 0.00000000e+000f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, 1.01835015e-001f }, { 7.08127349e-002f, 0.00000000e+000f, 0.00000000e+000f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, 1.29099445e-001f } }, + { { 1.39644596e-001f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, -1.29099445e-001f } }, + { { 1.39644596e-001f, 0.00000000e+000f, 0.00000000e+000f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, 1.01835015e-001f }, { 7.08127349e-002f, 0.00000000e+000f, 0.00000000e+000f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, 1.29099445e-001f } }, + { { 1.39644596e-001f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, -1.29099445e-001f } }, + { { 1.40852210e-001f, 1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f, 7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f, 9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f, -7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f, -9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, 7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, 9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, 1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.43315266e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } }, }; + const ALfloat (*Ambi3DMatrix)[2][MAX_AMBI_COEFFS] = AmbiMap ? Ambi3DMatrixHOA : Ambi3DMatrixFOA; -/* Change this to 2 for dual-band HRTF processing. May require a higher quality +/* Set this to 2 for dual-band HRTF processing. May require a higher quality * band-splitter, or better calculation of the new IR length to deal with the * tail generated by the filter. */ @@ -186,9 +204,7 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ ALuint max_length = 0; ALuint i, j, c, b; - assert(NumChannels == 4); - - for(c = 0;c < COUNTOF(Ambi3DPoints);c++) + for(c = 0;c < HRTF_AMBI_CHAN_COUNT;c++) { ALuint evidx, azidx; ALuint evoffset; @@ -215,7 +231,7 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ memset(temps, 0, sizeof(temps)); bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate); - for(c = 0;c < COUNTOF(Ambi3DMatrix);c++) + for(c = 0;c < HRTF_AMBI_CHAN_COUNT;c++) { const ALshort *fir; ALuint delay; @@ -240,11 +256,12 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ delay = Hrtf->delays[lidx[c]] - min_delay; for(i = 0;i < NumChannels;++i) { + const ALsizei a = AmbiMap ? AmbiMap[i] : i; for(b = 0;b < NUM_BANDS;b++) { ALuint k = 0; for(j = delay;j < HRIR_LENGTH;++j) - coeffs[i][j][0] += temps[b][k++] * Ambi3DMatrix[c][b][i]; + coeffs[i][j][0] += temps[b][k++] * Ambi3DMatrix[c][b][a]; } } max_length = maxu(max_length, minu(delay + Hrtf->irSize, HRIR_LENGTH)); @@ -269,11 +286,12 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ delay = Hrtf->delays[ridx[c]] - min_delay; for(i = 0;i < NumChannels;++i) { + const ALsizei a = AmbiMap ? AmbiMap[i] : i; for(b = 0;b < NUM_BANDS;b++) { ALuint k = 0; for(j = delay;j < HRIR_LENGTH;++j) - coeffs[i][j][1] += temps[b][k++] * Ambi3DMatrix[c][b][i]; + coeffs[i][j][1] += temps[b][k++] * Ambi3DMatrix[c][b][a]; } } max_length = maxu(max_length, minu(delay + Hrtf->irSize, HRIR_LENGTH)); @@ -282,6 +300,7 @@ ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][ #undef NUM_BANDS return max_length; +#undef HRTF_AMBI_CHAN_COUNT } @@ -47,6 +47,6 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, * for first-order. Returns the maximum impulse-response length of the * generated coefficients. */ -ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels); +ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels, const ALuint *AmbiMap); #endif /* ALC_HRTF_H */ diff --git a/Alc/panning.c b/Alc/panning.c index 4e4caf46..de9b8cb1 100644 --- a/Alc/panning.c +++ b/Alc/panning.c @@ -756,25 +756,45 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuin } } -static void InitHrtfPanning(ALCdevice *device) +static void InitHrtfPanning(ALCdevice *device, bool hoa_mode) { - size_t count = 4; + static const ALuint map_foa[] = { 0, 1, 2, 3 }; + static const ALuint map_hoa[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + const ALuint *ambi_map = hoa_mode ? map_hoa : map_foa; + size_t count = hoa_mode ? COUNTOF(map_hoa) : COUNTOF(map_foa); ALuint i; + static_assert(COUNTOF(map_hoa) <= COUNTOF(device->Hrtf.Coeffs), "ALCdevice::Hrtf.Values/Coeffs size is too small"); + for(i = 0;i < count;i++) { device->Dry.Ambi.Map[i].Scale = 1.0f; - device->Dry.Ambi.Map[i].Index = i; + device->Dry.Ambi.Map[i].Index = ambi_map[i]; } device->Dry.CoeffCount = 0; device->Dry.NumChannels = count; - device->FOAOut.Ambi = device->Dry.Ambi; - device->FOAOut.CoeffCount = device->Dry.CoeffCount; + if(!hoa_mode) + { + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + } + else + { + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + for(i = 0;i < 4;i++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; + } + device->FOAOut.CoeffCount = 0; + + ambiup_reset(device->AmbiUp, device); + } memset(device->Hrtf.Coeffs, 0, sizeof(device->Hrtf.Coeffs)); device->Hrtf.IrSize = BuildBFormatHrtf(device->Hrtf.Handle, - device->Hrtf.Coeffs, device->Dry.NumChannels + device->Hrtf.Coeffs, device->Dry.NumChannels, hoa_mode ? ambi_map : NULL ); /* Round up to the nearest multiple of 8 */ @@ -895,8 +915,6 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf return; } - ambiup_free(device->AmbiUp); - device->AmbiUp = NULL; bformatdec_free(device->AmbiDecoder); device->AmbiDecoder = NULL; @@ -964,6 +982,8 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf if(device->Hrtf.Handle) { + bool hoa_mode; + device->Render_Mode = HrtfRender; if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode)) { @@ -975,11 +995,27 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf ERR("Unexpected hrtf-mode: %s\n", mode); } + if(device->Render_Mode == HrtfRender) + { + /* Don't bother with HOA when using full HRTF rendering. Nothing + * needs it, and it eases the CPU/memory load. + */ + ambiup_free(device->AmbiUp); + device->AmbiUp = NULL; + hoa_mode = false; + } + else + { + if(!device->AmbiUp) + device->AmbiUp = ambiup_alloc(); + hoa_mode = true; + } + TRACE("%s HRTF rendering enabled, using \"%s\"\n", ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"), al_string_get_cstr(device->Hrtf.Name) ); - InitHrtfPanning(device); + InitHrtfPanning(device, hoa_mode); return; } device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; @@ -987,6 +1023,9 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf no_hrtf: TRACE("HRTF disabled\n"); + ambiup_free(device->AmbiUp); + device->AmbiUp = NULL; + bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) || (hrtf_appreq == Hrtf_Enable)) ? 5 : 0; if(device->Type != Loopback) diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 5a5c3923..975dd11e 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -643,8 +643,8 @@ struct ALCdevice_struct const struct Hrtf *Handle; /* HRTF filter state for dry buffer content */ - alignas(16) ALfloat Values[4][HRIR_LENGTH][2]; - alignas(16) ALfloat Coeffs[4][HRIR_LENGTH][2]; + alignas(16) ALfloat Values[9][HRIR_LENGTH][2]; + alignas(16) ALfloat Coeffs[9][HRIR_LENGTH][2]; ALuint Offset; ALuint IrSize; } Hrtf; |