Use 2-channel UHJ for stereo output

This commit is contained in:
Chris Robinson 2016-02-26 16:09:06 -08:00
parent 67f086d1d4
commit ac91083ceb
5 changed files with 79 additions and 52 deletions

View File

@ -36,6 +36,7 @@
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "bs2b.h"
#include "uhjfilter.h"
#include "alu.h"
#include "compat.h"
@ -1846,6 +1847,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if((device->Flags&DEVICE_RUNNING))
return ALC_NO_ERROR;
al_free(device->Uhj_Encoder);
device->Uhj_Encoder = NULL;
al_free(device->Bs2b);
device->Bs2b = NULL;
al_free(device->DryBuffer);
device->DryBuffer = NULL;
@ -1971,9 +1978,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
if(hrtf_appreq == Hrtf_Enable)
device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
free(device->Bs2b);
device->Bs2b = NULL;
}
else
{
@ -2067,8 +2071,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->Hrtf_Mode = hrtf_mode;
device->Hrtf_Status = hrtf_status;
TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf_Name));
free(device->Bs2b);
device->Bs2b = NULL;
}
else
{
@ -2080,27 +2082,25 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
ConfigValueInt(al_string_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
if(bs2blevel > 0 && bs2blevel <= 6)
{
if(!device->Bs2b)
{
device->Bs2b = calloc(1, sizeof(*device->Bs2b));
bs2b_clear(device->Bs2b);
}
device->Bs2b = al_calloc(16, sizeof(*device->Bs2b));
bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
TRACE("BS2B enabled\n");
}
else
{
free(device->Bs2b);
device->Bs2b = NULL;
TRACE("BS2B disabled\n");
}
device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder));
}
}
aluInitPanning(device);
/* With HRTF, allocate two extra channels for the post-filter output. */
size = sizeof(device->DryBuffer[0]) * (device->NumChannels + (device->Hrtf ? 2 : 0));
size = device->NumChannels * sizeof(device->DryBuffer[0]);
if(device->Hrtf || device->Uhj_Encoder)
size += 2 * sizeof(device->DryBuffer[0]);
device->DryBuffer = al_calloc(16, size);
if(!device->DryBuffer)
{
@ -2235,9 +2235,12 @@ static ALCvoid FreeDevice(ALCdevice *device)
AL_STRING_DEINIT(device->Hrtf_Name);
FreeHrtfList(&device->Hrtf_List);
free(device->Bs2b);
al_free(device->Bs2b);
device->Bs2b = NULL;
al_free(device->Uhj_Encoder);
device->Uhj_Encoder = NULL;
AL_STRING_DEINIT(device->DeviceName);
al_free(device->DryBuffer);
@ -3336,6 +3339,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
device->Flags = 0;
device->Bs2b = NULL;
device->Uhj_Encoder = NULL;
VECTOR_INIT(device->Hrtf_List);
AL_STRING_INIT(device->Hrtf_Name);
device->Hrtf_Mode = DisabledHrtf;
@ -3782,6 +3786,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
VECTOR_INIT(device->Hrtf_List);
AL_STRING_INIT(device->Hrtf_Name);
device->Bs2b = NULL;
device->Uhj_Encoder = NULL;
device->Hrtf_Mode = DisabledHrtf;
AL_STRING_INIT(device->DeviceName);
device->DryBuffer = NULL;

View File

@ -34,6 +34,7 @@
#include "alu.h"
#include "bs2b.h"
#include "hrtf.h"
#include "uhjfilter.h"
#include "static_assert.h"
#include "mixer_defs.h"
@ -100,19 +101,18 @@ 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, 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.
/* NOTE: HRTF and UHJ are set up a bit special in the device. Normally 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, and the actual
* output is stored in DryBuffer[NumChannels] for the left channel and
* With HRTF or UHJ, 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.
* the virtual channels will have HRTF filters or UHJ encoding applied and
* written to the actual output.
*
* Sources that get mixed using HRTF directly (or that want to skip HRTF
* Sources that get mixed using HRTF directly (or that want to skip HRTF or UHJ
* completely) will need to offset the output buffer so that they skip the
* virtual output and write to the actual output channels. This is the reason
* you'll see
@ -327,9 +327,6 @@ ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const A
}, StereoMap[2] = {
{ FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
{ FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
}, StereoWideMap[2] = {
{ FrontLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
{ FrontRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
}, RearMap[2] = {
{ BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
{ BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
@ -459,13 +456,7 @@ ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const A
break;
case FmtStereo:
/* HACK: Place the stereo channels at +/-90 degrees when using non-
* HRTF stereo output. This helps reduce the "monoization" caused
* by them panning towards the center. */
if(Device->FmtChans == DevFmtStereo && !Device->Hrtf)
chans = StereoWideMap;
else
chans = StereoMap;
chans = StereoMap;
num_channels = 2;
break;
@ -584,7 +575,7 @@ ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const A
if(DirectChannels)
{
if(Device->Hrtf)
if(Device->Hrtf || Device->Uhj_Encoder)
{
/* DirectChannels with HRTF enabled. Skip the virtual channels
* and write FrontLeft and FrontRight inputs to the first and
@ -1384,7 +1375,7 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
SamplesToDo = minu(size, BUFFERSIZE);
for(c = 0;c < OutChannels;c++)
memset(OutBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
if(device->Hrtf)
if(device->Hrtf || device->Uhj_Encoder)
{
/* Set OutBuffer/OutChannels to correspond to the actual output
* with HRTF. Make sure to clear them too. */
@ -1485,17 +1476,25 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
}
device->Hrtf_Offset += SamplesToDo;
}
else if(device->Bs2b)
else
{
/* Apply binaural/crossfeed filter */
for(i = 0;i < SamplesToDo;i++)
if(device->Uhj_Encoder)
{
float samples[2];
samples[0] = device->DryBuffer[0][i];
samples[1] = device->DryBuffer[1][i];
bs2b_cross_feed(device->Bs2b, samples);
device->DryBuffer[0][i] = samples[0];
device->DryBuffer[1][i] = samples[1];
/* Encode to stereo-compatible 2-channel UHJ output. */
EncodeUhj2(device->Uhj_Encoder, OutBuffer, device->DryBuffer, SamplesToDo);
}
if(device->Bs2b)
{
/* Apply binaural/crossfeed filter */
for(i = 0;i < SamplesToDo;i++)
{
float samples[2];
samples[0] = OutBuffer[0][i];
samples[1] = OutBuffer[1][i];
bs2b_cross_feed(device->Bs2b, samples);
OutBuffer[0][i] = samples[0];
OutBuffer[1][i] = samples[1];
}
}
}

View File

@ -659,7 +659,7 @@ static ALvoid UpdateEchoLine(ALfloat echoTime, ALfloat decayTime, ALfloat diffus
}
// Update the early and late 3D panning gains.
static ALvoid UpdateHrtfPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALfloat EarlyGain, ALfloat LateGain, ALreverbState *State)
static ALvoid UpdateMixedPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALfloat EarlyGain, ALfloat LateGain, ALreverbState *State)
{
ALfloat DirGains[MAX_OUTPUT_CHANNELS];
ALfloat coeffs[MAX_AMBI_COEFFS];
@ -925,11 +925,11 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device
gain = props->Reverb.Gain * Slot->Gain * ReverbBoost;
// Update early and late 3D panning.
if(Device->Hrtf)
UpdateHrtfPanning(Device, props->Reverb.ReflectionsPan,
props->Reverb.LateReverbPan, gain,
props->Reverb.ReflectionsGain,
props->Reverb.LateReverbGain, State);
if(Device->Hrtf || Device->Uhj_Encoder)
UpdateMixedPanning(Device, props->Reverb.ReflectionsPan,
props->Reverb.LateReverbPan, gain,
props->Reverb.ReflectionsGain,
props->Reverb.LateReverbGain, State);
else if(Device->FmtChans == DevFmtBFormat3D)
Update3DPanning(Device, props->Reverb.ReflectionsPan,
props->Reverb.LateReverbPan, gain,

View File

@ -484,6 +484,10 @@ ALvoid aluInitPanning(ALCdevice *device)
{ 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 } },
}, BFormat2D[3] = {
{ BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
{ BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } },
{ BFormatY, { 0.0f, 0.0f, 1.0f, 0.0f } },
}, BFormat3D[4] = {
{ BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
{ BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } },
@ -493,6 +497,7 @@ ALvoid aluInitPanning(ALCdevice *device)
const ChannelMap *chanmap = NULL;
ALfloat ambiscale = 1.0f;
size_t count = 0;
ALuint i;
device->AmbiScale = 1.0f;
memset(device->AmbiCoeffs, 0, sizeof(device->AmbiCoeffs));
@ -514,7 +519,6 @@ ALvoid aluInitPanning(ALCdevice *device)
{ LowerBackLeft, DEG2RAD(-135.0f), DEG2RAD(-45.0f) },
{ LowerBackRight, DEG2RAD( 135.0f), DEG2RAD(-45.0f) },
};
ALuint i;
count = COUNTOF(Cube8Cfg);
chanmap = Cube8Cfg;
@ -536,6 +540,22 @@ ALvoid aluInitPanning(ALCdevice *device)
}
return;
}
if(device->Uhj_Encoder)
{
count = COUNTOF(BFormat2D);
chanmap = BFormat2D;
ambiscale = FIRST_ORDER_SCALE;
for(i = 0;i < count;i++)
device->ChannelName[i] = chanmap[i].ChanName;
for(;i < MAX_OUTPUT_CHANNELS;i++)
device->ChannelName[i] = InvalidChannel;
SetChannelMap(device->ChannelName, device->AmbiCoeffs, chanmap, count,
&device->NumChannels, AL_TRUE);
device->AmbiScale = ambiscale;
return;
}
if(LoadChannelSetup(device))
return;

View File

@ -470,6 +470,9 @@ struct ALCdevice_struct
HrtfParams Hrtf_Params[MAX_OUTPUT_CHANNELS];
ALuint Hrtf_Offset;
/* UHJ encoder state */
struct Uhj2Encoder *Uhj_Encoder;
// Stereo-to-binaural filter
struct bs2b *Bs2b;