aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2016-03-15 05:08:05 -0700
committerChris Robinson <[email protected]>2016-03-15 05:08:05 -0700
commit53fadf54977a3312db66e7e086c9b01d9162ae29 (patch)
tree1a6696bd14200484ae4dfa955f32830deb3b2a46
parent64cb21cb9ff08d222d00eedc38fbc0970543bac3 (diff)
Add a dual-band ambisonic decoder
This uses a virtual B-Format buffer for mixing, and then uses a dual-band decoder for improved positional quality. This currently only works with first- order output since first-order input (from the AL_EXT_BFROMAT extension) would not sound correct when fed through a second- or third-order decoder. This also does not currently implement near-field compensation since near-field rendering effects are not implemented.
-rw-r--r--Alc/ALc.c17
-rw-r--r--Alc/ALu.c8
-rw-r--r--Alc/bformatdec.c286
-rw-r--r--Alc/bformatdec.h14
-rw-r--r--Alc/effects/reverb.c20
-rw-r--r--Alc/panning.c131
-rw-r--r--CMakeLists.txt1
-rw-r--r--OpenAL32/Include/alMain.h3
-rw-r--r--OpenAL32/Include/alu.h3
9 files changed, 467 insertions, 16 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index 8ede026a..f51757e8 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -37,6 +37,8 @@
#include "alError.h"
#include "bs2b.h"
#include "uhjfilter.h"
+#include "bformatdec.h"
+#include "ambdec.h"
#include "alu.h"
#include "compat.h"
@@ -2116,12 +2118,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
}
}
- aluInitPanning(device);
+ aluInitPanning(device, NULL);
- /* With HRTF, allocate two extra channels for the post-filter output. */
+ /* Allocate extra channels for any post-filter output. */
size = device->Dry.NumChannels * sizeof(device->Dry.Buffer[0]);
- if(device->Hrtf || device->Uhj_Encoder)
- size += 2 * sizeof(device->Dry.Buffer[0]);
+ if(device->Hrtf || device->Uhj_Encoder || device->AmbiDecoder)
+ size += ChannelsFromDevFmt(device->FmtChans) * sizeof(device->Dry.Buffer[0]);
device->Dry.Buffer = al_calloc(16, size);
if(!device->Dry.Buffer)
{
@@ -2129,12 +2131,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
return ALC_INVALID_DEVICE;
}
- if(device->Hrtf || device->Uhj_Encoder)
+ if(device->Hrtf || device->Uhj_Encoder || device->AmbiDecoder)
{
device->VirtOut.Buffer = device->Dry.Buffer;
device->VirtOut.NumChannels = device->Dry.NumChannels;
device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels;
- device->RealOut.NumChannels = 2;
+ device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans);
}
else
{
@@ -2277,6 +2279,9 @@ static ALCvoid FreeDevice(ALCdevice *device)
al_free(device->Uhj_Encoder);
device->Uhj_Encoder = NULL;
+ bformatdec_free(device->AmbiDecoder);
+ device->AmbiDecoder = NULL;
+
AL_STRING_DEINIT(device->DeviceName);
al_free(device->Dry.Buffer);
diff --git a/Alc/ALu.c b/Alc/ALu.c
index d7f68c03..5064a588 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -35,6 +35,7 @@
#include "bs2b.h"
#include "hrtf.h"
#include "uhjfilter.h"
+#include "bformatdec.h"
#include "static_assert.h"
#include "mixer_defs.h"
@@ -1462,6 +1463,13 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
device->Hrtf_Offset += SamplesToDo;
}
}
+ else if(device->AmbiDecoder)
+ {
+ bformatdec_process(device->AmbiDecoder,
+ device->RealOut.Buffer, device->RealOut.NumChannels,
+ device->VirtOut.Buffer, SamplesToDo
+ );
+ }
else
{
if(device->Uhj_Encoder)
diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c
new file mode 100644
index 00000000..d917f803
--- /dev/null
+++ b/Alc/bformatdec.c
@@ -0,0 +1,286 @@
+
+#include "config.h"
+
+#include "bformatdec.h"
+#include "ambdec.h"
+#include "alu.h"
+
+
+typedef struct BandSplitter {
+ ALfloat coeff;
+ ALfloat lp_z1;
+ ALfloat lp_z2;
+ ALfloat hp_z1;
+} BandSplitter;
+
+static void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
+{
+ ALfloat w = freq_mult * F_TAU;
+ ALfloat cw = cosf(w);
+ if(cw > FLT_EPSILON)
+ splitter->coeff = (sinf(w) - 1.0f) / cw;
+ else
+ splitter->coeff = cw * -0.5f;
+
+ splitter->lp_z1 = 0.0f;
+ splitter->lp_z2 = 0.0f;
+ splitter->hp_z1 = 0.0f;
+}
+
+static void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+ const ALfloat *input, ALuint count)
+{
+ ALfloat coeff, d, x;
+ ALuint i;
+
+ coeff = splitter->coeff*0.5f + 0.5f;
+ for(i = 0;i < count;i++)
+ {
+ x = input[i];
+
+ d = (x - splitter->lp_z1) * coeff;
+ x = splitter->lp_z1 + d;
+ splitter->lp_z1 = x + d;
+
+ d = (x - splitter->lp_z2) * coeff;
+ x = splitter->lp_z2 + d;
+ splitter->lp_z2 = x + d;
+
+ lpout[i] = x;
+ }
+
+ coeff = splitter->coeff;
+ for(i = 0;i < count;i++)
+ {
+ x = input[i];
+
+ d = x - coeff*splitter->hp_z1;
+ x = splitter->hp_z1 + coeff*d;
+ splitter->hp_z1 = d;
+
+ hpout[i] = x - lpout[i];
+ }
+}
+
+
+static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+};
+static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
+ 1.000000000f, /* ACN 0 (W), sqrt(1) */
+ 1.732050808f, /* ACN 1 (Y), sqrt(3) */
+ 1.732050808f, /* ACN 2 (Z), sqrt(3) */
+ 1.732050808f, /* ACN 3 (X), sqrt(3) */
+ 2.236067978f, /* ACN 4 (V), sqrt(5) */
+ 2.236067978f, /* ACN 5 (T), sqrt(5) */
+ 2.236067978f, /* ACN 6 (R), sqrt(5) */
+ 2.236067978f, /* ACN 7 (S), sqrt(5) */
+ 2.236067978f, /* ACN 8 (U), sqrt(5) */
+ 2.645751311f, /* ACN 9 (Q), sqrt(7) */
+ 2.645751311f, /* ACN 10 (O), sqrt(7) */
+ 2.645751311f, /* ACN 11 (M), sqrt(7) */
+ 2.645751311f, /* ACN 12 (K), sqrt(7) */
+ 2.645751311f, /* ACN 13 (L), sqrt(7) */
+ 2.645751311f, /* ACN 14 (N), sqrt(7) */
+ 2.645751311f, /* ACN 15 (P), sqrt(7) */
+};
+static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
+ 1.414213562f, /* ACN 0 (W), sqrt(2) */
+ 1.732050808f, /* ACN 1 (Y), sqrt(3) */
+ 1.732050808f, /* ACN 2 (Z), sqrt(3) */
+ 1.732050808f, /* ACN 3 (X), sqrt(3) */
+ 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
+ 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
+ 2.236067978f, /* ACN 6 (R), sqrt(5) */
+ 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
+ 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
+ 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
+ 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
+ 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
+ 2.645751311f, /* ACN 12 (K), sqrt(7) */
+ 2.231093404f, /* ACN 13 (L), sqrt(224/45) */
+ 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
+ 2.091650066f, /* ACN 15 (P), sqrt(35/8) */
+};
+
+
+/* NOTE: Low-frequency (LF) fields and BandSplitter filters are unused with
+ * single-band decoding
+ */
+typedef struct BFormatDec {
+ alignas(16) ALfloat MatrixHF[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+ alignas(16) ALfloat MatrixLF[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+
+ BandSplitter XOver[MAX_AMBI_COEFFS];
+
+ ALfloat (*Samples)[BUFFERSIZE];
+ /* These two alias into Samples */
+ ALfloat (*SamplesHF)[BUFFERSIZE];
+ ALfloat (*SamplesLF)[BUFFERSIZE];
+
+ ALuint NumChannels;
+ ALboolean DualBand;
+} BFormatDec;
+
+BFormatDec *bformatdec_alloc()
+{
+ return al_calloc(16, sizeof(BFormatDec));
+}
+
+void bformatdec_free(BFormatDec *dec)
+{
+ if(dec)
+ {
+ al_free(dec->Samples);
+ dec->Samples = NULL;
+ dec->SamplesHF = NULL;
+ dec->SamplesLF = NULL;
+
+ memset(dec, 0, sizeof(*dec));
+ al_free(dec);
+ }
+}
+
+void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS])
+{
+ const ALfloat *coeff_scale = UnitScale;
+ ALfloat ratio;
+ ALuint i;
+
+ al_free(dec->Samples);
+ dec->Samples = NULL;
+ dec->SamplesHF = NULL;
+ dec->SamplesLF = NULL;
+
+ dec->NumChannels = chancount;
+ dec->Samples = al_calloc(16, dec->NumChannels * conf->FreqBands *
+ sizeof(dec->Samples[0]));
+ dec->SamplesHF = dec->Samples;
+ dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
+
+ if(conf->CoeffScale == ADS_SN3D)
+ coeff_scale = SN3D2N3DScale;
+ else if(conf->CoeffScale == ADS_FuMa)
+ coeff_scale = FuMa2N3DScale;
+
+ if(conf->FreqBands == 1)
+ {
+ dec->DualBand = AL_FALSE;
+ ratio = 1.0f;
+ }
+ else
+ {
+ dec->DualBand = AL_TRUE;
+
+ ratio = conf->XOverFreq / (ALfloat)srate;
+ for(i = 0;i < MAX_AMBI_COEFFS;i++)
+ bandsplit_init(&dec->XOver[i], ratio);
+
+ ratio = powf(10.0f, conf->XOverRatio / 40.0f);
+ memset(dec->MatrixLF, 0, sizeof(dec->MatrixLF));
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ ALuint chan = chanmap[i];
+ ALuint j, k = 0;
+
+ for(j = 0;j < 1;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixLF[chan][j] = conf->LFMatrix[i][k++] / coeff_scale[j] *
+ conf->LFOrderGain[0] / ratio;
+ }
+ for(;j < 4;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixLF[chan][j] = conf->LFMatrix[i][k++] / coeff_scale[j] *
+ conf->LFOrderGain[1] / ratio;
+ }
+ for(;j < 9;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixLF[chan][j] = conf->LFMatrix[i][k++] / coeff_scale[j] *
+ conf->LFOrderGain[2] / ratio;
+ }
+ for(;j < 16;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixLF[chan][j] = conf->LFMatrix[i][k++] / coeff_scale[j] *
+ conf->LFOrderGain[3] / ratio;
+ }
+ }
+ }
+
+ memset(dec->MatrixHF, 0, sizeof(dec->MatrixHF));
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ ALuint chan = chanmap[i];
+ ALuint j, k = 0;
+
+ for(j = 0;j < 1;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixHF[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+ conf->HFOrderGain[0] * ratio;
+ }
+ for(;j < 4;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixHF[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+ conf->HFOrderGain[1] * ratio;
+ }
+ for(;j < 9;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixHF[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+ conf->HFOrderGain[2] * ratio;
+ }
+ for(;j < 16;j++)
+ {
+ if((conf->ChanMask&(1<<j)))
+ dec->MatrixHF[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+ conf->HFOrderGain[3] * ratio;
+ }
+ }
+}
+
+
+static void apply_row(ALfloat *out, const ALfloat *mtx, ALfloat (*restrict in)[BUFFERSIZE], ALuint inchans, ALuint todo)
+{
+ ALuint c, i;
+
+ for(c = 0;c < inchans;c++)
+ {
+ ALfloat gain = mtx[c];
+ if(!(gain > GAIN_SILENCE_THRESHOLD))
+ continue;
+ for(i = 0;i < todo;i++)
+ out[i] += in[c][i] * gain;
+ }
+}
+
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
+{
+ ALuint chan, i;
+
+ if(dec->DualBand)
+ {
+ for(i = 0;i < dec->NumChannels;i++)
+ bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
+ InSamples[i], SamplesToDo);
+
+ for(chan = 0;chan < OutChannels;chan++)
+ {
+ apply_row(OutBuffer[chan], dec->MatrixHF[chan], dec->SamplesHF,
+ dec->NumChannels, SamplesToDo);
+ apply_row(OutBuffer[chan], dec->MatrixLF[chan], dec->SamplesLF,
+ dec->NumChannels, SamplesToDo);
+ }
+ }
+ else
+ {
+ for(chan = 0;chan < OutChannels;chan++)
+ apply_row(OutBuffer[chan], dec->MatrixHF[chan], InSamples,
+ dec->NumChannels, SamplesToDo);
+ }
+}
diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h
new file mode 100644
index 00000000..a322f41d
--- /dev/null
+++ b/Alc/bformatdec.h
@@ -0,0 +1,14 @@
+#ifndef BFORMATDEC_H
+#define BFORMATDEC_H
+
+#include "alMain.h"
+
+struct AmbDecConf;
+struct BFormatDec;
+
+struct BFormatDec *bformatdec_alloc();
+void bformatdec_free(struct BFormatDec *dec);
+void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS]);
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo);
+
+#endif /* BFORMATDEC_H */
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index e68e6e19..cf31ba62 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -87,10 +87,11 @@ typedef struct ALreverbState {
ALuint Offset[4];
// The gain for each output channel based on 3D panning.
- // NOTE: With HRTF, we may be rendering to the dry buffer and the
- // "real" buffer. The dry buffer may be using all 8 output channels, so
- // we need two extra for the real stereo output.
- ALfloat PanGain[4][MAX_OUTPUT_CHANNELS+2];
+ // NOTE: With certain output modes, we may be rendering to the dry
+ // buffer and the "real" buffer. The two combined may be using more
+ // than 8 output channels, so we need some extra for the real output
+ // too.
+ ALfloat PanGain[4][MAX_OUTPUT_CHANNELS+4];
} Early;
// Decorrelator delay line.
@@ -128,8 +129,8 @@ typedef struct ALreverbState {
ALfloat LpSample[4];
// The gain for each output channel based on 3D panning.
- // NOTE: Add two in case of HRTF (see note about early pan).
- ALfloat PanGain[4][MAX_OUTPUT_CHANNELS+2];
+ // NOTE: Add some extra in case (see note about early pan).
+ ALfloat PanGain[4][MAX_OUTPUT_CHANNELS+4];
} Late;
struct {
@@ -371,7 +372,10 @@ static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Dev
/* WARNING: This assumes the real output follows the virtual output in the
* device's DryBuffer.
*/
- State->ExtraChannels = (Device->Hrtf || Device->Uhj_Encoder) ? 2 : 0;
+ if(Device->Hrtf || Device->Uhj_Encoder || Device->AmbiDecoder)
+ State->ExtraChannels = ChannelsFromDevFmt(Device->FmtChans);
+ else
+ State->ExtraChannels = 0;
// Calculate the modulation filter coefficient. Notice that the exponent
// is calculated given the current sample rate. This ensures that the
@@ -938,7 +942,7 @@ 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 || Device->Uhj_Encoder)
+ if(Device->Hrtf || Device->Uhj_Encoder || Device->AmbiDecoder)
UpdateMixedPanning(Device, props->Reverb.ReflectionsPan,
props->Reverb.LateReverbPan, gain,
props->Reverb.ReflectionsGain,
diff --git a/Alc/panning.c b/Alc/panning.c
index 49bc05b2..df504292 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -30,6 +30,8 @@
#include "alAuxEffectSlot.h"
#include "alu.h"
#include "bool.h"
+#include "ambdec.h"
+#include "bformatdec.h"
extern inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat coeffs[MAX_AMBI_COEFFS]);
@@ -252,6 +254,88 @@ static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeff
*outcount = i;
}
+static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALuint speakermap[MAX_OUTPUT_CHANNELS])
+{
+ ALuint i;
+
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ int c = -1;
+
+ /* NOTE: AmbDec does not define any standard speaker names, however
+ * for this to work we have to by able to find the output channel
+ * the speaker definition corresponds to. Therefore, OpenAL Soft
+ * requires these channel labels to be recognized:
+ *
+ * LF = Front left
+ * RF = Front right
+ * LS = Side left
+ * RS = Side right
+ * LB = Back left
+ * RB = Back right
+ * CE = Front center
+ * CB = Back center
+ *
+ * Additionally, surround51 will acknowledge back speakers for side
+ * channels, and surround51rear will acknowledge side speakers for
+ * back channels, to avoid issues with an ambdec expecting 5.1 to
+ * use the side channels when the device is configured for back,
+ * and vice-versa.
+ */
+ if(al_string_cmp_cstr(conf->Speakers[i].Name, "LF") == 0)
+ c = GetChannelIdxByName(device->RealOut, FrontLeft);
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RF") == 0)
+ c = GetChannelIdxByName(device->RealOut, FrontRight);
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CE") == 0)
+ c = GetChannelIdxByName(device->RealOut, FrontCenter);
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LS") == 0)
+ {
+ if(device->FmtChans == DevFmtX51Rear)
+ c = GetChannelIdxByName(device->RealOut, BackLeft);
+ else
+ c = GetChannelIdxByName(device->RealOut, SideLeft);
+ }
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RS") == 0)
+ {
+ if(device->FmtChans == DevFmtX51Rear)
+ c = GetChannelIdxByName(device->RealOut, BackRight);
+ else
+ c = GetChannelIdxByName(device->RealOut, SideRight);
+ }
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LB") == 0)
+ {
+ if(device->FmtChans == DevFmtX51)
+ c = GetChannelIdxByName(device->RealOut, SideLeft);
+ else
+ c = GetChannelIdxByName(device->RealOut, BackLeft);
+ }
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RB") == 0)
+ {
+ if(device->FmtChans == DevFmtX51)
+ c = GetChannelIdxByName(device->RealOut, SideRight);
+ else
+ c = GetChannelIdxByName(device->RealOut, BackRight);
+ }
+ else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CB") == 0)
+ c = GetChannelIdxByName(device->RealOut, BackCenter);
+ else
+ {
+ ERR("AmbDec speaker label \"%s\" not recognized\n",
+ al_string_get_cstr(conf->Speakers[i].Name));
+ return false;
+ }
+ if(c == -1)
+ {
+ ERR("Failed to lookup AmbDec speaker label %s\n",
+ al_string_get_cstr(conf->Speakers[i].Name));
+ return false;
+ }
+ speakermap[i] = c;
+ }
+
+ return true;
+}
+
static bool LoadChannelSetup(ALCdevice *device)
{
static const enum Channel mono_chans[1] = {
@@ -432,7 +516,7 @@ static bool LoadChannelSetup(ALCdevice *device)
return true;
}
-ALvoid aluInitPanning(ALCdevice *device)
+ALvoid aluInitPanning(ALCdevice *device, const AmbDecConf *conf)
{
/* NOTE: These decoder coefficients are using FuMa channel ordering and
* normalization, since that's what was produced by the Ambisonic Decoder
@@ -556,6 +640,51 @@ ALvoid aluInitPanning(ALCdevice *device)
return;
}
+ if(device->AmbiDecoder)
+ {
+ /* NOTE: This is ACN/N3D ordering and scaling, rather than FuMa. */
+ static const ChannelMap Ambi3D[4] = {
+ { BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
+ { BFormatY, { 0.0f, 1.0f, 0.0f, 0.0f } },
+ { BFormatZ, { 0.0f, 0.0f, 1.0f, 0.0f } },
+ { BFormatX, { 0.0f, 0.0f, 0.0f, 1.0f } },
+ };
+ ALuint speakermap[MAX_OUTPUT_CHANNELS];
+
+ if(conf->ChanMask > 0xffff)
+ {
+ ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf->ChanMask);
+ goto ambi_fail;
+ }
+ if(conf->ChanMask > 0xf)
+ {
+ ERR("Only first-order is supported for HQ decoding (mask 0x%04x, max 0xf)\n",
+ conf->ChanMask);
+ goto ambi_fail;
+ }
+
+ if(!MakeSpeakerMap(device, conf, speakermap))
+ goto ambi_fail;
+ bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap);
+
+ count = COUNTOF(Ambi3D);
+ chanmap = Ambi3D;
+ ambiscale = FIRST_ORDER_SCALE;
+
+ for(i = 0;i < count;i++)
+ device->Dry.ChannelName[i] = chanmap[i].ChanName;
+ for(;i < MAX_OUTPUT_CHANNELS;i++)
+ device->Dry.ChannelName[i] = InvalidChannel;
+ SetChannelMap(device->Dry.ChannelName, device->Dry.AmbiCoeffs, chanmap, count,
+ &device->Dry.NumChannels, AL_FALSE);
+ device->Dry.AmbiScale = ambiscale;
+
+ return;
+
+ ambi_fail:
+ bformatdec_free(device->AmbiDecoder);
+ device->AmbiDecoder = NULL;
+ }
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
device->Dry.ChannelName[i] = device->RealOut.ChannelName[i];
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d815c2aa..c3e17967 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -645,6 +645,7 @@ SET(ALC_OBJS Alc/ALc.c
Alc/hrtf.c
Alc/uhjfilter.c
Alc/ambdec.c
+ Alc/bformatdec.c
Alc/panning.c
Alc/mixer.c
Alc/mixer_c.c
diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h
index 86311761..3ce95801 100644
--- a/OpenAL32/Include/alMain.h
+++ b/OpenAL32/Include/alMain.h
@@ -476,6 +476,9 @@ struct ALCdevice_struct
// Stereo-to-binaural filter
struct bs2b *Bs2b;
+ /* High quality Ambisonic decoder */
+ struct BFormatDec *AmbiDecoder;
+
/* Rendering mode. */
enum RenderMode Render_Mode;
diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h
index 08f25204..dfc9284f 100644
--- a/OpenAL32/Include/alu.h
+++ b/OpenAL32/Include/alu.h
@@ -33,6 +33,7 @@
extern "C" {
#endif
+struct AmbDecConf;
struct ALsource;
struct ALvoice;
struct ALeffectslot;
@@ -280,7 +281,7 @@ inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat v
void aluInitMixer(void);
-ALvoid aluInitPanning(ALCdevice *Device);
+ALvoid aluInitPanning(ALCdevice *Device, const struct AmbDecConf *conf);
void aluInitEffectPanning(struct ALeffectslot *slot);