From 147274f165b5cf8f1f004567c777e0e274c9e228 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 23 Mar 2016 12:53:36 -0700 Subject: [PATCH] Up-sample first-order content when using a higher order HQ decoder --- Alc/ALu.c | 8 ++++++ Alc/bformatdec.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Alc/bformatdec.h | 5 ++++ Alc/panning.c | 2 -- 4 files changed, 101 insertions(+), 2 deletions(-) diff --git a/Alc/ALu.c b/Alc/ALu.c index b5b7de9c..206aceda 100644 --- a/Alc/ALu.c +++ b/Alc/ALu.c @@ -1367,6 +1367,9 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) memset(device->VirtOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); for(c = 0;c < device->RealOut.NumChannels;c++) memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); + if(device->Dry.Buffer != device->FOAOut.Buffer) + for(c = 0;c < device->FOAOut.NumChannels;c++) + memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); V0(device->Backend,lock)(); @@ -1467,6 +1470,11 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) } else if(device->AmbiDecoder) { + if(device->VirtOut.Buffer != device->FOAOut.Buffer) + bformatdec_upSample(device->AmbiDecoder, + device->VirtOut.Buffer, device->FOAOut.Buffer, + device->FOAOut.NumChannels, SamplesToDo + ); bformatdec_process(device->AmbiDecoder, device->RealOut.Buffer, device->RealOut.NumChannels, device->VirtOut.Buffer, SamplesToDo diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c index 37be67e4..ab148d1b 100644 --- a/Alc/bformatdec.c +++ b/Alc/bformatdec.c @@ -5,6 +5,8 @@ #include "ambdec.h" #include "alu.h" +#include "threads.h" + typedef struct BandSplitter { ALfloat coeff; @@ -105,6 +107,46 @@ static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = { }; +static const ALfloat SquareMatrixHF[4][MAX_AMBI_COEFFS] = { + { 0.353553f, 0.204094f, 0.0f, 0.204094f }, + { 0.353553f, -0.204094f, 0.0f, 0.204094f }, + { 0.353553f, 0.204094f, 0.0f, -0.204094f }, + { 0.353553f, -0.204094f, 0.0f, -0.204094f }, +}; +static ALfloat SquareEncoder[4][MAX_AMBI_COEFFS]; + +static const ALfloat CubeMatrixHF[8][MAX_AMBI_COEFFS] = { + { 0.25f, 0.14425f, 0.14425f, 0.14425f }, + { 0.25f, -0.14425f, 0.14425f, 0.14425f }, + { 0.25f, 0.14425f, 0.14425f, -0.14425f }, + { 0.25f, -0.14425f, 0.14425f, -0.14425f }, + { 0.25f, 0.14425f, -0.14425f, 0.14425f }, + { 0.25f, -0.14425f, -0.14425f, 0.14425f }, + { 0.25f, 0.14425f, -0.14425f, -0.14425f }, + { 0.25f, -0.14425f, -0.14425f, -0.14425f }, +}; +static ALfloat CubeEncoder[8][MAX_AMBI_COEFFS]; + +static alonce_flag encoder_inited = AL_ONCE_FLAG_INIT; + +static void init_encoder(void) +{ + CalcXYZCoeffs(-0.577350269f, 0.577350269f, -0.577350269f, CubeEncoder[0]); + CalcXYZCoeffs( 0.577350269f, 0.577350269f, -0.577350269f, CubeEncoder[1]); + CalcXYZCoeffs(-0.577350269f, 0.577350269f, 0.577350269f, CubeEncoder[2]); + CalcXYZCoeffs( 0.577350269f, 0.577350269f, 0.577350269f, CubeEncoder[3]); + CalcXYZCoeffs(-0.577350269f, -0.577350269f, -0.577350269f, CubeEncoder[4]); + CalcXYZCoeffs( 0.577350269f, -0.577350269f, -0.577350269f, CubeEncoder[5]); + CalcXYZCoeffs(-0.577350269f, -0.577350269f, 0.577350269f, CubeEncoder[6]); + CalcXYZCoeffs( 0.577350269f, -0.577350269f, 0.577350269f, CubeEncoder[7]); + + CalcXYZCoeffs(-0.707106781f, 0.0f, -0.707106781f, SquareEncoder[0]); + CalcXYZCoeffs( 0.707106781f, 0.0f, -0.707106781f, SquareEncoder[1]); + CalcXYZCoeffs(-0.707106781f, 0.0f, 0.707106781f, SquareEncoder[2]); + CalcXYZCoeffs( 0.707106781f, 0.0f, 0.707106781f, SquareEncoder[3]); +} + + /* NOTE: Low-frequency (LF) fields and BandSplitter filters are unused with * single-band decoding */ @@ -119,12 +161,19 @@ typedef struct BFormatDec { ALfloat (*SamplesHF)[BUFFERSIZE]; ALfloat (*SamplesLF)[BUFFERSIZE]; + struct { + const ALfloat (*restrict MatrixHF)[MAX_AMBI_COEFFS]; + const ALfloat (*restrict Encoder)[MAX_AMBI_COEFFS]; + ALuint NumChannels; + } UpSampler; + ALuint NumChannels; ALboolean DualBand; } BFormatDec; BFormatDec *bformatdec_alloc() { + alcall_once(&encoder_inited, init_encoder); return al_calloc(16, sizeof(BFormatDec)); } @@ -172,6 +221,19 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, else if(conf->CoeffScale == ADS_FuMa) coeff_scale = FuMa2N3DScale; + if((conf->ChanMask & ~0x831b)) + { + dec->UpSampler.MatrixHF = CubeMatrixHF; + dec->UpSampler.Encoder = (const ALfloat(*)[MAX_AMBI_COEFFS])CubeEncoder; + dec->UpSampler.NumChannels = 8; + } + else + { + dec->UpSampler.MatrixHF = SquareMatrixHF; + dec->UpSampler.Encoder = (const ALfloat(*)[MAX_AMBI_COEFFS])SquareEncoder; + dec->UpSampler.NumChannels = 4; + } + if(conf->FreqBands == 1) { dec->DualBand = AL_FALSE; @@ -264,3 +326,29 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU dec->NumChannels, SamplesToDo); } } + + +void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo) +{ + ALuint i, j, k; + + /* This up-sampler is very simplistic. It essentially decodes the first- + * order content to a square channel array (or cube if height is desired), + * then encodes those points onto the higher order soundfield. + */ + for(k = 0;k < dec->UpSampler.NumChannels;k++) + { + memset(dec->Samples[0], 0, SamplesToDo*sizeof(ALfloat)); + apply_row(dec->Samples[0], dec->UpSampler.MatrixHF[k], InSamples, + InChannels, SamplesToDo); + + for(j = 0;j < dec->NumChannels;j++) + { + ALfloat gain = dec->UpSampler.Encoder[k][j]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + for(i = 0;i < SamplesToDo;i++) + OutBuffer[j][i] += dec->Samples[0][i] * gain; + } + } +} diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h index 5f17d711..623e7b09 100644 --- a/Alc/bformatdec.h +++ b/Alc/bformatdec.h @@ -10,6 +10,11 @@ struct BFormatDec *bformatdec_alloc(); void bformatdec_free(struct BFormatDec *dec); int bformatdec_getOrder(const struct BFormatDec *dec); void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS]); + +/* Decodes the ambisonic input to the given output channels. */ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo); +/* 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); + #endif /* BFORMATDEC_H */ diff --git a/Alc/panning.c b/Alc/panning.c index 031b518d..9c504b04 100644 --- a/Alc/panning.c +++ b/Alc/panning.c @@ -641,8 +641,6 @@ ALvoid aluInitPanning(ALCdevice *device) if(!MakeSpeakerMap(device, &conf, speakermap)) goto ambi_fail; - if(conf.ChanMask > 0xf) - ERR("Second-order HQ decoding does not currently handle first-order sources\n"); count = (conf.ChanMask > 0xf) ? COUNTOF(Ambi3D) : 4; chanmap = Ambi3D; ambiscale = 1.0f; -- 2.11.4.GIT