aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2016-07-30 09:29:21 -0700
committerChris Robinson <[email protected]>2016-07-30 09:29:21 -0700
commit33a84f17ac78ac1f77b48fbd1193fafab5ca14e7 (patch)
tree66055c0a89d22552b856038434333b2ca2eb06cc /Alc
parentb5b3ea95f899410a5392fb633ace74c10bbd9921 (diff)
Add a stand-alone upsampler for higher-order ambisonic oputput
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c7
-rw-r--r--Alc/ALu.c7
-rw-r--r--Alc/bformatdec.c85
-rw-r--r--Alc/bformatdec.h10
-rw-r--r--Alc/panning.c19
5 files changed, 118 insertions, 10 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index 13f881dd..8c6381ad 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -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);
diff --git a/Alc/ALu.c b/Alc/ALu.c
index c1478b50..d1a12672 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -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);
diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c
index d6cf25e2..3c175b33 100644
--- a/Alc/bformatdec.c
+++ b/Alc/bformatdec.c
@@ -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;
+ }
+ }
+}
diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h
index 3ad06373..be5a69f6 100644
--- a/Alc/bformatdec.h
+++ b/Alc/bformatdec.h
@@ -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 */
diff --git a/Alc/panning.c b/Alc/panning.c
index 0c1bf06f..80909564 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -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;