Add a stand-alone upsampler for higher-order ambisonic oputput
This commit is contained in:
@@ -2040,7 +2040,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
size += (ChannelsFromDevFmt(device->FmtChans)+4) * sizeof(device->Dry.Buffer[0]);
|
||||
else if(device->Hrtf || device->Uhj_Encoder || device->AmbiDecoder)
|
||||
size += ChannelsFromDevFmt(device->FmtChans) * sizeof(device->Dry.Buffer[0]);
|
||||
else if(device->FmtChans == DevFmtAmbi2 || device->FmtChans == DevFmtAmbi3)
|
||||
else if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)
|
||||
size += 4 * sizeof(device->Dry.Buffer[0]);
|
||||
device->Dry.Buffer = al_calloc(16, size);
|
||||
if(!device->Dry.Buffer)
|
||||
@@ -2061,7 +2061,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
}
|
||||
|
||||
if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2) ||
|
||||
device->FmtChans == DevFmtAmbi2 || device->FmtChans == DevFmtAmbi3)
|
||||
(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3))
|
||||
{
|
||||
/* Higher-order rendering requires upsampling first-order content, so
|
||||
* make sure to mix it separately.
|
||||
@@ -2211,6 +2211,9 @@ static ALCvoid FreeDevice(ALCdevice *device)
|
||||
bformatdec_free(device->AmbiDecoder);
|
||||
device->AmbiDecoder = NULL;
|
||||
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
|
||||
AL_STRING_DEINIT(device->DeviceName);
|
||||
|
||||
al_free(device->Dry.Buffer);
|
||||
|
||||
@@ -1534,6 +1534,13 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
||||
device->Dry.Buffer, SamplesToDo
|
||||
);
|
||||
}
|
||||
else if(device->AmbiUp)
|
||||
{
|
||||
ambiup_process(device->AmbiUp,
|
||||
device->RealOut.Buffer, device->RealOut.NumChannels,
|
||||
device->FOAOut.Buffer, SamplesToDo
|
||||
);
|
||||
}
|
||||
else if(device->Uhj_Encoder)
|
||||
{
|
||||
int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
|
||||
|
||||
+77
-8
@@ -130,6 +130,16 @@ static const ALfloat SquareMatrix[4][FB_Max][MAX_AMBI_COEFFS] = {
|
||||
};
|
||||
static ALfloat SquareEncoder[4][MAX_AMBI_COEFFS];
|
||||
|
||||
static const ALfloat CubePoints[8][3] = {
|
||||
{ -0.577350269f, 0.577350269f, -0.577350269f },
|
||||
{ 0.577350269f, 0.577350269f, -0.577350269f },
|
||||
{ -0.577350269f, 0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, 0.577350269f, 0.577350269f },
|
||||
{ -0.577350269f, -0.577350269f, -0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, -0.577350269f },
|
||||
{ -0.577350269f, -0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, 0.577350269f },
|
||||
};
|
||||
static const ALfloat CubeMatrix[8][FB_Max][MAX_AMBI_COEFFS] = {
|
||||
{ { 0.25f, 0.14425f, 0.14425f, 0.14425f }, { 0.125f, 0.125f, 0.125f, 0.125f } },
|
||||
{ { 0.25f, -0.14425f, 0.14425f, 0.14425f }, { 0.125f, -0.125f, 0.125f, 0.125f } },
|
||||
@@ -167,14 +177,8 @@ static void init_bformatdec(void)
|
||||
|
||||
MixMatrixRow = SelectMixer();
|
||||
|
||||
CalcXYZCoeffs(-0.577350269f, 0.577350269f, -0.577350269f, 0.0f, CubeEncoder[0]);
|
||||
CalcXYZCoeffs( 0.577350269f, 0.577350269f, -0.577350269f, 0.0f, CubeEncoder[1]);
|
||||
CalcXYZCoeffs(-0.577350269f, 0.577350269f, 0.577350269f, 0.0f, CubeEncoder[2]);
|
||||
CalcXYZCoeffs( 0.577350269f, 0.577350269f, 0.577350269f, 0.0f, CubeEncoder[3]);
|
||||
CalcXYZCoeffs(-0.577350269f, -0.577350269f, -0.577350269f, 0.0f, CubeEncoder[4]);
|
||||
CalcXYZCoeffs( 0.577350269f, -0.577350269f, -0.577350269f, 0.0f, CubeEncoder[5]);
|
||||
CalcXYZCoeffs(-0.577350269f, -0.577350269f, 0.577350269f, 0.0f, CubeEncoder[6]);
|
||||
CalcXYZCoeffs( 0.577350269f, -0.577350269f, 0.577350269f, 0.0f, CubeEncoder[7]);
|
||||
for(i = 0;i < COUNTOF(CubePoints);i++)
|
||||
CalcDirectionCoeffs(CubePoints[i], 0.0f, CubeEncoder[i]);
|
||||
|
||||
CalcXYZCoeffs(-0.707106781f, 0.0f, -0.707106781f, 0.0f, SquareEncoder[0]);
|
||||
CalcXYZCoeffs( 0.707106781f, 0.0f, -0.707106781f, 0.0f, SquareEncoder[1]);
|
||||
@@ -588,3 +592,68 @@ void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct AmbiUpsampler {
|
||||
alignas(16) ALfloat SamplesHF[4][BUFFERSIZE];
|
||||
alignas(16) ALfloat SamplesLF[4][BUFFERSIZE];
|
||||
|
||||
alignas(16) ALfloat ChannelMix[BUFFERSIZE];
|
||||
|
||||
BandSplitter XOver[4];
|
||||
|
||||
ALfloat Gains[8][MAX_OUTPUT_CHANNELS];
|
||||
ALuint NumChannels;
|
||||
} AmbiUpsampler;
|
||||
|
||||
AmbiUpsampler *ambiup_alloc()
|
||||
{
|
||||
alcall_once(&bformatdec_inited, init_bformatdec);
|
||||
return al_calloc(16, sizeof(AmbiUpsampler));
|
||||
}
|
||||
|
||||
void ambiup_free(struct AmbiUpsampler *ambiup)
|
||||
{
|
||||
al_free(ambiup);
|
||||
}
|
||||
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
|
||||
{
|
||||
ALfloat ratio;
|
||||
ALuint i;
|
||||
|
||||
ratio = 400.0f / (ALfloat)device->Frequency;
|
||||
for(i = 0;i < 4;i++)
|
||||
bandsplit_init(&ambiup->XOver[i], ratio);
|
||||
|
||||
ambiup->NumChannels = COUNTOF(CubePoints);
|
||||
for(i = 0;i < ambiup->NumChannels;i++)
|
||||
ComputePanningGains(device->Dry, CubeEncoder[i], 1.0f, ambiup->Gains[i]);
|
||||
}
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
|
||||
{
|
||||
ALuint i, j, k;
|
||||
|
||||
for(i = 0;i < 4;i++)
|
||||
bandsplit_process(&ambiup->XOver[i], ambiup->SamplesHF[i], ambiup->SamplesLF[i],
|
||||
InSamples[i], SamplesToDo);
|
||||
|
||||
for(k = 0;k < ambiup->NumChannels;k++)
|
||||
{
|
||||
memset(ambiup->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixMatrixRow(ambiup->ChannelMix, CubeMatrix[k][FB_HighFreq],
|
||||
ambiup->SamplesHF, 4, SamplesToDo);
|
||||
MixMatrixRow(ambiup->ChannelMix, CubeMatrix[k][FB_LowFreq],
|
||||
ambiup->SamplesLF, 4, SamplesToDo);
|
||||
|
||||
for(j = 0;j < OutChannels;j++)
|
||||
{
|
||||
ALfloat gain = ambiup->Gains[k][j];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[j][i] += ambiup->ChannelMix[i] * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
struct AmbDecConf;
|
||||
struct BFormatDec;
|
||||
struct AmbiUpsampler;
|
||||
|
||||
enum BFormatDecFlags {
|
||||
BFDF_DistanceComp = 1<<0
|
||||
@@ -21,4 +22,13 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU
|
||||
/* Up-samples a first-order input to the decoder's configuration. */
|
||||
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo);
|
||||
|
||||
/* Stand-alone first-order upsampler. Kept here because it shares some stuff
|
||||
* with bformatdec.
|
||||
*/
|
||||
struct AmbiUpsampler *ambiup_alloc();
|
||||
void ambiup_free(struct AmbiUpsampler *ambiup);
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device);
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo);
|
||||
|
||||
#endif /* BFORMATDEC_H */
|
||||
|
||||
@@ -622,6 +622,9 @@ static void InitPanning(ALCdevice *device)
|
||||
device->Dry.CoeffCount = 0;
|
||||
device->Dry.NumChannels = count;
|
||||
|
||||
/* FOA output is always ACN+N3D for higher-order ambisonic output. The
|
||||
* upsampler expects this and will convert it for output.
|
||||
*/
|
||||
memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
@@ -629,6 +632,8 @@ static void InitPanning(ALCdevice *device)
|
||||
device->FOAOut.Ambi.Map[i].Index = i;
|
||||
}
|
||||
device->FOAOut.CoeffCount = 0;
|
||||
|
||||
ambiup_reset(device->AmbiUp, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -903,6 +908,8 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
|
||||
if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
|
||||
{
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
if(!device->AmbiDecoder)
|
||||
device->AmbiDecoder = bformatdec_alloc();
|
||||
}
|
||||
@@ -910,6 +917,16 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
{
|
||||
bformatdec_free(device->AmbiDecoder);
|
||||
device->AmbiDecoder = NULL;
|
||||
if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)
|
||||
{
|
||||
if(!device->AmbiUp)
|
||||
device->AmbiUp = ambiup_alloc();
|
||||
}
|
||||
else
|
||||
{
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(!pconf)
|
||||
@@ -923,6 +940,8 @@ 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;
|
||||
|
||||
|
||||
@@ -597,9 +597,12 @@ struct ALCdevice_struct
|
||||
/* High quality Ambisonic decoder */
|
||||
struct BFormatDec *AmbiDecoder;
|
||||
|
||||
// Stereo-to-binaural filter
|
||||
/* Stereo-to-binaural filter */
|
||||
struct bs2b *Bs2b;
|
||||
|
||||
/* First-order ambisonic upsampler for higher-order output */
|
||||
struct AmbiUpsampler *AmbiUp;
|
||||
|
||||
/* Rendering mode. */
|
||||
enum RenderMode Render_Mode;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user