diff options
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/ALc.c | 17 | ||||
-rw-r--r-- | Alc/ALu.c | 8 | ||||
-rw-r--r-- | Alc/bformatdec.c | 286 | ||||
-rw-r--r-- | Alc/bformatdec.h | 14 | ||||
-rw-r--r-- | Alc/effects/reverb.c | 20 | ||||
-rw-r--r-- | Alc/panning.c | 131 |
6 files changed, 461 insertions, 15 deletions
@@ -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); @@ -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]; |