From 33a84f17ac78ac1f77b48fbd1193fafab5ca14e7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 30 Jul 2016 09:29:21 -0700 Subject: Add a stand-alone upsampler for higher-order ambisonic oputput --- Alc/ALc.c | 7 ++-- Alc/ALu.c | 7 ++++ Alc/bformatdec.c | 85 ++++++++++++++++++++++++++++++++++++++++++----- Alc/bformatdec.h | 10 ++++++ Alc/panning.c | 19 +++++++++++ OpenAL32/Include/alMain.h | 5 ++- 6 files changed, 122 insertions(+), 11 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; diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 34533da9..d07cb589 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -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; -- cgit v1.2.3