diff options
Diffstat (limited to 'Alc/panning.c')
-rw-r--r-- | Alc/panning.c | 1392 |
1 files changed, 1037 insertions, 355 deletions
diff --git a/Alc/panning.c b/Alc/panning.c index 6305bff7..2c0f3bf2 100644 --- a/Alc/panning.c +++ b/Alc/panning.c @@ -27,19 +27,24 @@ #include <assert.h> #include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" +#include "alAuxEffectSlot.h" #include "alu.h" +#include "alconfig.h" #include "bool.h" +#include "ambdec.h" +#include "bformatdec.h" +#include "filters/splitter.h" +#include "uhjfilter.h" +#include "bs2b.h" -#define ZERO_ORDER_SCALE 0.0f -#define FIRST_ORDER_SCALE 1.0f -#define SECOND_ORDER_SCALE (1.0f / 1.22474f) -#define THIRD_ORDER_SCALE (1.0f / 1.30657f) +extern inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); +extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); +extern inline float ScaleAzimuthFront(float azimuth, float scale); +extern inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); -static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = { +static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = { 0, /* W */ 3, /* X */ 1, /* Y */ @@ -57,71 +62,21 @@ static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = { 15, /* P */ 9, /* Q */ }; - -/* NOTE: These are scale factors as applied to Ambisonics content. FuMa - * decoder coefficients should be divided by these values to get N3D decoder - * coefficients. - */ -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) */ +static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 }; -void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) -{ - ALuint i; - - for(i = 0;i < device->NumChannels;i++) - { - // The W coefficients are based on a mathematical average of the - // output. The square root of the base average provides for a more - // perceptual average volume, better suited to non-directional gains. - gains[i] = sqrtf(device->AmbiCoeffs[i][0]) * ingain; - } - for(;i < MAX_OUTPUT_CHANNELS;i++) - gains[i] = 0.0f; -} - -void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread, + ALfloat coeffs[MAX_AMBI_COEFFS]) { - ALfloat dir[3] = { - sinf(angle) * cosf(elevation), - sinf(elevation), - -cosf(angle) * cosf(elevation) - }; - ComputeDirectionalGains(device, dir, ingain, gains); -} - -void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) -{ - ALfloat coeffs[MAX_AMBI_COEFFS]; - ALuint i, j; - /* Convert from OpenAL coords to Ambisonics. */ - ALfloat x = -dir[2]; - ALfloat y = -dir[0]; - ALfloat z = dir[1]; - /* Zeroth-order */ coeffs[0] = 1.0f; /* ACN 0 = 1 */ /* First-order */ - coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */ - coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */ - coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */ + coeffs[1] = SQRTF_3 * y; /* ACN 1 = sqrt(3) * Y */ + coeffs[2] = SQRTF_3 * z; /* ACN 2 = sqrt(3) * Z */ + coeffs[3] = SQRTF_3 * x; /* ACN 3 = sqrt(3) * X */ /* Second-order */ coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */ coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */ @@ -137,34 +92,92 @@ void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfl coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */ coeffs[15] = 2.091650066f * x * (x*x - 3.0f*y*y); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */ - for(i = 0;i < device->NumChannels;i++) + if(spread > 0.0f) { - float gain = 0.0f; - for(j = 0;j < MAX_AMBI_COEFFS;j++) - gain += device->AmbiCoeffs[i][j]*coeffs[j]; - gains[i] = gain * ingain; + /* Implement the spread by using a spherical source that subtends the + * angle spread. See: + * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3 + * + * When adjusted for N3D normalization instead of SN3D, these + * calculations are: + * + * ZH0 = -sqrt(pi) * (-1+ca); + * ZH1 = 0.5*sqrt(pi) * sa*sa; + * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1); + * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1); + * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3); + * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1); + * + * The gain of the source is compensated for size, so that the + * loundness doesn't depend on the spread. Thus: + * + * ZH0 = 1.0f; + * ZH1 = 0.5f * (ca+1.0f); + * ZH2 = 0.5f * (ca+1.0f)*ca; + * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f); + * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca; + * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f); + */ + ALfloat ca = cosf(spread * 0.5f); + /* Increase the source volume by up to +3dB for a full spread. */ + ALfloat scale = sqrtf(1.0f + spread/F_TAU); + + ALfloat ZH0_norm = scale; + ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale; + ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale; + ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale; + + /* Zeroth-order */ + coeffs[0] *= ZH0_norm; + /* First-order */ + coeffs[1] *= ZH1_norm; + coeffs[2] *= ZH1_norm; + coeffs[3] *= ZH1_norm; + /* Second-order */ + coeffs[4] *= ZH2_norm; + coeffs[5] *= ZH2_norm; + coeffs[6] *= ZH2_norm; + coeffs[7] *= ZH2_norm; + coeffs[8] *= ZH2_norm; + /* Third-order */ + coeffs[9] *= ZH3_norm; + coeffs[10] *= ZH3_norm; + coeffs[11] *= ZH3_norm; + coeffs[12] *= ZH3_norm; + coeffs[13] *= ZH3_norm; + coeffs[14] *= ZH3_norm; + coeffs[15] *= ZH3_norm; } - for(;i < MAX_OUTPUT_CHANNELS;i++) - gains[i] = 0.0f; } -void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) + +void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) { - ALuint i, j; + ALsizei i, j; - for(i = 0;i < device->NumChannels;i++) + for(i = 0;i < numchans;i++) { float gain = 0.0f; - for(j = 0;j < 4;j++) - gain += device->AmbiCoeffs[i][j] * mtx[j]; - gains[i] = gain * ingain; + for(j = 0;j < numcoeffs;j++) + gain += chancoeffs[i][j]*coeffs[j]; + gains[i] = clampf(gain, 0.0f, 1.0f) * ingain; } for(;i < MAX_OUTPUT_CHANNELS;i++) gains[i] = 0.0f; } +void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + ALsizei i; + + for(i = 0;i < numchans;i++) + gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain; + for(;i < MAX_OUTPUT_CHANNELS;i++) + gains[i] = 0.0f; +} + -DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel) +static inline const char *GetLabelFromChannel(enum Channel channel) { switch(channel) { @@ -178,10 +191,31 @@ DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel) case SideLeft: return "side-left"; case SideRight: return "side-right"; - case BFormatW: return "bformat-w"; - case BFormatX: return "bformat-x"; - case BFormatY: return "bformat-y"; - case BFormatZ: return "bformat-z"; + case UpperFrontLeft: return "upper-front-left"; + case UpperFrontRight: return "upper-front-right"; + case UpperBackLeft: return "upper-back-left"; + case UpperBackRight: return "upper-back-right"; + case LowerFrontLeft: return "lower-front-left"; + case LowerFrontRight: return "lower-front-right"; + case LowerBackLeft: return "lower-back-left"; + case LowerBackRight: return "lower-back-right"; + + case Aux0: return "aux-0"; + case Aux1: return "aux-1"; + case Aux2: return "aux-2"; + case Aux3: return "aux-3"; + case Aux4: return "aux-4"; + case Aux5: return "aux-5"; + case Aux6: return "aux-6"; + case Aux7: return "aux-7"; + case Aux8: return "aux-8"; + case Aux9: return "aux-9"; + case Aux10: return "aux-10"; + case Aux11: return "aux-11"; + case Aux12: return "aux-12"; + case Aux13: return "aux-13"; + case Aux14: return "aux-14"; + case Aux15: return "aux-15"; case InvalidChannel: break; } @@ -194,364 +228,1012 @@ typedef struct ChannelMap { ChannelConfig Config; } ChannelMap; -static void SetChannelMap(ALCdevice *device, const ChannelMap *chanmap, size_t count, ALfloat ambiscale, ALboolean isfuma) +static void SetChannelMap(const enum Channel devchans[MAX_OUTPUT_CHANNELS], + ChannelConfig *ambicoeffs, const ChannelMap *chanmap, + ALsizei count, ALsizei *outcount) { - size_t j, k; - ALuint i; + ALsizei maxchans = 0; + ALsizei i, j; - device->AmbiScale = ambiscale; - for(i = 0;i < MAX_OUTPUT_CHANNELS && device->ChannelName[i] != InvalidChannel;i++) + for(i = 0;i < count;i++) { - if(device->ChannelName[i] == LFE) + ALint idx = GetChannelIndex(devchans, chanmap[i].ChanName); + if(idx < 0) { - for(j = 0;j < MAX_AMBI_COEFFS;j++) - device->AmbiCoeffs[i][j] = 0.0f; + ERR("Failed to find %s channel in device\n", + GetLabelFromChannel(chanmap[i].ChanName)); continue; } - for(j = 0;j < count;j++) + maxchans = maxi(maxchans, idx+1); + for(j = 0;j < MAX_AMBI_COEFFS;j++) + ambicoeffs[idx][j] = chanmap[i].Config[j]; + } + *outcount = mini(maxchans, MAX_OUTPUT_CHANNELS); +} + +static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + ALsizei i; + + for(i = 0;i < conf->NumSpeakers;i++) + { + enum Channel ch; + int chidx = -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(alstr_cmp_cstr(conf->Speakers[i].Name, "LF") == 0) + ch = FrontLeft; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RF") == 0) + ch = FrontRight; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CE") == 0) + ch = FrontCenter; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LS") == 0) + { + if(device->FmtChans == DevFmtX51Rear) + ch = BackLeft; + else + ch = SideLeft; + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RS") == 0) + { + if(device->FmtChans == DevFmtX51Rear) + ch = BackRight; + else + ch = SideRight; + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LB") == 0) + { + if(device->FmtChans == DevFmtX51) + ch = SideLeft; + else + ch = BackLeft; + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RB") == 0) + { + if(device->FmtChans == DevFmtX51) + ch = SideRight; + else + ch = BackRight; + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CB") == 0) + ch = BackCenter; + else { - if(device->ChannelName[i] == chanmap[j].ChanName) + const char *name = alstr_get_cstr(conf->Speakers[i].Name); + unsigned int n; + char c; + + if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16) + ch = Aux0+n; + else { - if(isfuma) - { - /* Reformat FuMa -> ACN/N3D */ - for(k = 0;k < MAX_AMBI_COEFFS;++k) - { - ALuint acn = FuMa2ACN[k]; - device->AmbiCoeffs[i][acn] = chanmap[j].Config[k] / FuMa2N3DScale[acn]; - } - } - else - { - for(k = 0;k < MAX_AMBI_COEFFS;++k) - device->AmbiCoeffs[i][k] = chanmap[j].Config[k]; - } - break; + ERR("AmbDec speaker label \"%s\" not recognized\n", name); + return false; } } - if(j == count) - ERR("Failed to match %s channel (%u) in config\n", GetLabelFromChannel(device->ChannelName[i]), i); + chidx = GetChannelIdxByName(&device->RealOut, ch); + if(chidx == -1) + { + ERR("Failed to lookup AmbDec speaker label %s\n", + alstr_get_cstr(conf->Speakers[i].Name)); + return false; + } + speakermap[i] = chidx; } - device->NumChannels = i; + + return true; } -static bool LoadChannelSetup(ALCdevice *device) + +static const ChannelMap MonoCfg[1] = { + { FrontCenter, { 1.0f } }, +}, StereoCfg[2] = { + { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.52305643e-2f } }, + { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.52305643e-2f } }, +}, QuadCfg[4] = { + { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, -2.04124145e-1f } }, + { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, 2.04124145e-1f } }, + { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, 2.04124145e-1f } }, + { BackRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } }, +}, X51SideCfg[4] = { + { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, + { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { SideRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, +}, X51RearCfg[4] = { + { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, + { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { BackRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, +}, X61Cfg[6] = { + { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, 0.0f, -4.39996780e-2f, -2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } }, + { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 0.0f, 1.59658796e-1f, 6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } }, + { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 0.0f, 1.59658796e-1f, -6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } }, + { SideRight, { 2.04460341e-1f, -2.17177926e-1f, 0.0f, -4.39996780e-2f, 2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } }, + { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, 0.0f, -2.50000094e-1f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 6.05133395e-2f } }, +}, X71Cfg[6] = { + { BackLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, -1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, 1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, 1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, +}; + +static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, + const ALsizei *restrict chans_per_order) { - static const enum Channel mono_chans[1] = { - FrontCenter - }, stereo_chans[2] = { - FrontLeft, FrontRight - }, quad_chans[4] = { - FrontLeft, FrontRight, - BackLeft, BackRight - }, surround51_chans[5] = { - FrontLeft, FrontRight, FrontCenter, - SideLeft, SideRight - }, surround51rear_chans[5] = { - FrontLeft, FrontRight, FrontCenter, - BackLeft, BackRight - }, surround61_chans[6] = { - FrontLeft, FrontRight, - FrontCenter, BackCenter, - SideLeft, SideRight - }, surround71_chans[7] = { - FrontLeft, FrontRight, FrontCenter, - BackLeft, BackRight, - SideLeft, SideRight - }; - ChannelMap chanmap[MAX_OUTPUT_CHANNELS]; - const enum Channel *channels = NULL; - const char *layout = NULL; - ALfloat ambiscale = 1.0f; - size_t count = 0; - int isfuma; - int order; - size_t i; + const char *devname = alstr_get_cstr(device->DeviceName); + ALsizei i; + + if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f) + { + /* NFC is only used when AvgSpeakerDist is greater than 0, and can only + * be used when rendering to an ambisonic buffer. + */ + device->AvgSpeakerDist = minf(ctrl_dist, 10.0f); + TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist); + + for(i = 0;i < order+1;i++) + device->NumChannelsPerOrder[i] = chans_per_order[i]; + for(;i < MAX_AMBI_ORDER+1;i++) + device->NumChannelsPerOrder[i] = 0; + } +} + +static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + const char *devname = alstr_get_cstr(device->DeviceName); + ALfloat maxdist = 0.0f; + size_t total = 0; + ALsizei i; + + for(i = 0;i < conf->NumSpeakers;i++) + maxdist = maxf(maxdist, conf->Speakers[i].Distance); + + if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f) + { + ALfloat srate = (ALfloat)device->Frequency; + for(i = 0;i < conf->NumSpeakers;i++) + { + ALsizei chan = speakermap[i]; + ALfloat delay; + + /* Distance compensation only delays in steps of the sample rate. + * This is a bit less accurate since the delay time falls to the + * nearest sample time, but it's far simpler as it doesn't have to + * deal with phase offsets. This means at 48khz, for instance, the + * distance delay will be in steps of about 7 millimeters. + */ + delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC * + srate + 0.5f); + if(delay >= (ALfloat)MAX_DELAY_LENGTH) + ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n", + alstr_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH); + + device->ChannelDelay[chan].Length = (ALsizei)clampf( + delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1) + ); + device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist; + TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan, + alstr_get_cstr(conf->Speakers[i].Name), device->ChannelDelay[chan].Length, + device->ChannelDelay[chan].Gain + ); + + /* Round up to the next 4th sample, so each channel buffer starts + * 16-byte aligned. + */ + total += RoundUp(device->ChannelDelay[chan].Length, 4); + } + } + + if(total > 0) + { + device->ChannelDelay[0].Buffer = al_calloc(16, total * sizeof(ALfloat)); + for(i = 1;i < MAX_OUTPUT_CHANNELS;i++) + { + size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4); + device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len; + } + } +} + +static void InitPanning(ALCdevice *device) +{ + const ChannelMap *chanmap = NULL; + ALsizei coeffcount = 0; + ALsizei count = 0; + ALsizei i, j; switch(device->FmtChans) { case DevFmtMono: - layout = "mono"; - channels = mono_chans; - count = COUNTOF(mono_chans); + count = COUNTOF(MonoCfg); + chanmap = MonoCfg; + coeffcount = 1; break; + case DevFmtStereo: - layout = "stereo"; - channels = stereo_chans; - count = COUNTOF(stereo_chans); + count = COUNTOF(StereoCfg); + chanmap = StereoCfg; + coeffcount = 4; break; + case DevFmtQuad: - layout = "quad"; - channels = quad_chans; - count = COUNTOF(quad_chans); + count = COUNTOF(QuadCfg); + chanmap = QuadCfg; + coeffcount = 4; break; + case DevFmtX51: - layout = "surround51"; - channels = surround51_chans; - count = COUNTOF(surround51_chans); + count = COUNTOF(X51SideCfg); + chanmap = X51SideCfg; + coeffcount = 9; break; + case DevFmtX51Rear: - layout = "surround51rear"; - channels = surround51rear_chans; - count = COUNTOF(surround51rear_chans); + count = COUNTOF(X51RearCfg); + chanmap = X51RearCfg; + coeffcount = 9; break; + case DevFmtX61: - layout = "surround61"; - channels = surround61_chans; - count = COUNTOF(surround61_chans); + count = COUNTOF(X61Cfg); + chanmap = X61Cfg; + coeffcount = 9; break; + case DevFmtX71: - layout = "surround71"; - channels = surround71_chans; - count = COUNTOF(surround71_chans); + count = COUNTOF(X71Cfg); + chanmap = X71Cfg; + coeffcount = 16; break; - case DevFmtBFormat3D: + + case DevFmtAmbi3D: break; } - if(!layout) - return false; - else + if(device->FmtChans == DevFmtAmbi3D) { - char name[32] = {0}; - const char *type; - char eol; - - snprintf(name, sizeof(name), "%s/type", layout); - if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", name, &type)) - return false; + const char *devname = alstr_get_cstr(device->DeviceName); + const ALsizei *acnmap = (device->AmbiLayout == AmbiLayout_FuMa) ? FuMa2ACN : ACN2ACN; + const ALfloat *n3dscale = (device->AmbiScale == AmbiNorm_FuMa) ? FuMa2N3DScale : + (device->AmbiScale == AmbiNorm_SN3D) ? SN3D2N3DScale : + /*(device->AmbiScale == AmbiNorm_N3D) ?*/ N3D2N3DScale; + ALfloat nfc_delay = 0.0f; - if(sscanf(type, " %31[^: ] : %d%c", name, &order, &eol) != 2) + count = (device->AmbiOrder == 3) ? 16 : + (device->AmbiOrder == 2) ? 9 : + (device->AmbiOrder == 1) ? 4 : 1; + for(i = 0;i < count;i++) { - ERR("Invalid type value '%s' (expected name:order) for layout %s\n", type, layout); - return false; + ALsizei acn = acnmap[i]; + device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn]; + device->Dry.Ambi.Map[i].Index = acn; } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; - if(strcasecmp(name, "fuma") == 0) - isfuma = 1; - else if(strcasecmp(name, "n3d") == 0) - isfuma = 0; + if(device->AmbiOrder < 2) + { + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; + } else { - ERR("Unhandled type name '%s' (expected FuMa or N3D) for layout %s\n", name, layout); - return false; + ALfloat w_scale=1.0f, xyz_scale=1.0f; + + /* 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++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; + } + device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = 4; + + if(device->AmbiOrder >= 3) + { + w_scale = W_SCALE_3H3P; + xyz_scale = XYZ_SCALE_3H3P; + } + else + { + w_scale = W_SCALE_2H2P; + xyz_scale = XYZ_SCALE_2H2P; + } + ambiup_reset(device->AmbiUp, device, w_scale, xyz_scale); } - if(order == 3) - ambiscale = THIRD_ORDER_SCALE; - else if(order == 2) - ambiscale = SECOND_ORDER_SCALE; - else if(order == 1) - ambiscale = FIRST_ORDER_SCALE; - else if(order == 0) - ambiscale = ZERO_ORDER_SCALE; - else + if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f) { - ERR("Unhandled type order %d (expected 0, 1, 2, or 3) for layout %s\n", order, layout); - return false; + static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = { + 1, 3, 5, 7 + }; + nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f); + InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC, + device->AmbiOrder, chans_per_order); } } + else + { + ALfloat w_scale, xyz_scale; - for(i = 0;i < count;i++) + SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, + chanmap, count, &device->Dry.NumChannels); + device->Dry.CoeffCount = coeffcount; + + w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE_3H0P : + (device->Dry.CoeffCount > 4) ? W_SCALE_2H0P : 1.0f; + xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE_3H0P : + (device->Dry.CoeffCount > 4) ? XYZ_SCALE_2H0P : 1.0f; + + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + for(i = 0;i < device->Dry.NumChannels;i++) + { + device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale; + for(j = 1;j < 4;j++) + device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale; + } + device->FOAOut.CoeffCount = 4; + device->FOAOut.NumChannels = 0; + } + device->RealOut.NumChannels = 0; +} + +static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + ChannelMap chanmap[MAX_OUTPUT_CHANNELS]; + const ALfloat *coeff_scale = N3D2N3DScale; + ALfloat w_scale = 1.0f; + ALfloat xyz_scale = 1.0f; + ALsizei i, j; + + if(conf->FreqBands != 1) + ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n", + conf->XOverFreq); + + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + if(conf->ChanMask > 0x1ff) + { + w_scale = W_SCALE_3H3P; + xyz_scale = XYZ_SCALE_3H3P; + } + else if(conf->ChanMask > 0xf) + { + w_scale = W_SCALE_2H2P; + xyz_scale = XYZ_SCALE_2H2P; + } + } + else + { + if(conf->ChanMask > 0x1ff) + { + w_scale = W_SCALE_3H0P; + xyz_scale = XYZ_SCALE_3H0P; + } + else if(conf->ChanMask > 0xf) + { + w_scale = W_SCALE_2H0P; + xyz_scale = XYZ_SCALE_2H0P; + } + } + + if(conf->CoeffScale == ADS_SN3D) + coeff_scale = SN3D2N3DScale; + else if(conf->CoeffScale == ADS_FuMa) + coeff_scale = FuMa2N3DScale; + + for(i = 0;i < conf->NumSpeakers;i++) { - float coeffs[MAX_AMBI_COEFFS] = {0.0f}; - const char *channame; - char chanlayout[32]; - const char *value; - int props = 0; - char eol = 0; - int j; + ALsizei chan = speakermap[i]; + ALfloat gain; + ALsizei k = 0; - chanmap[i].ChanName = channels[i]; - channame = GetLabelFromChannel(channels[i]); + for(j = 0;j < MAX_AMBI_COEFFS;j++) + chanmap[i].Config[j] = 0.0f; - snprintf(chanlayout, sizeof(chanlayout), "%s/%s", layout, channame); - if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", chanlayout, &value)) + chanmap[i].ChanName = device->RealOut.ChannelName[chan]; + for(j = 0;j < MAX_AMBI_COEFFS;j++) { - ERR("Missing channel %s\n", channame); - return false; + if(j == 0) gain = conf->HFOrderGain[0]; + else if(j == 1) gain = conf->HFOrderGain[1]; + else if(j == 4) gain = conf->HFOrderGain[2]; + else if(j == 9) gain = conf->HFOrderGain[3]; + if((conf->ChanMask&(1<<j))) + chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain; } - if(order == 3) - props = sscanf(value, " %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %c", - &coeffs[0], &coeffs[1], &coeffs[2], &coeffs[3], - &coeffs[4], &coeffs[5], &coeffs[6], &coeffs[7], - &coeffs[8], &coeffs[9], &coeffs[10], &coeffs[11], - &coeffs[12], &coeffs[13], &coeffs[14], &coeffs[15], - &eol - ); - else if(order == 2) - props = sscanf(value, " %f %f %f %f %f %f %f %f %f %c", - &coeffs[0], &coeffs[1], &coeffs[2], - &coeffs[3], &coeffs[4], &coeffs[5], - &coeffs[6], &coeffs[7], &coeffs[8], - &eol - ); - else if(order == 1) - props = sscanf(value, " %f %f %f %f %c", - &coeffs[0], &coeffs[1], - &coeffs[2], &coeffs[3], - &eol - ); - else if(order == 0) - props = sscanf(value, " %f %c", &coeffs[0], &eol); - if(props == 0) + } + + SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap, + conf->NumSpeakers, &device->Dry.NumChannels); + device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 : + (conf->ChanMask > 0xf) ? 9 : 4; + + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + for(i = 0;i < device->Dry.NumChannels;i++) + { + device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale; + for(j = 1;j < 4;j++) + device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale; + } + device->FOAOut.CoeffCount = 4; + device->FOAOut.NumChannels = 0; + + device->RealOut.NumChannels = 0; + + InitDistanceComp(device, conf, speakermap); +} + +static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + static const ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 }; + static const ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 }; + ALfloat avg_dist; + ALsizei count; + ALsizei i; + + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + count = (conf->ChanMask > 0x1ff) ? 16 : + (conf->ChanMask > 0xf) ? 9 : 4; + for(i = 0;i < count;i++) { - ERR("Failed to parse option %s properties\n", chanlayout); - return false; + device->Dry.Ambi.Map[i].Scale = 1.0f; + device->Dry.Ambi.Map[i].Index = i; } + } + else + { + static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 }; - if(props > (order+1)*(order+1)) + count = (conf->ChanMask > 0x1ff) ? 7 : + (conf->ChanMask > 0xf) ? 5 : 3; + for(i = 0;i < count;i++) { - ERR("Excess elements in option %s (expected %d)\n", chanlayout, (order+1)*(order+1)); - return false; + device->Dry.Ambi.Map[i].Scale = 1.0f; + device->Dry.Ambi.Map[i].Index = map[i]; } + } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; + + TRACE("Enabling %s-band %s-order%s ambisonic decoder\n", + (conf->FreqBands == 1) ? "single" : "dual", + (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first", + (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : "" + ); + bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap); - for(j = 0;j < MAX_AMBI_COEFFS;++j) - chanmap[i].Config[j] = coeffs[j]; + if(!(conf->ChanMask > 0xf)) + { + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; } - SetChannelMap(device, chanmap, count, ambiscale, isfuma); - return true; + else + { + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + count = 4; + for(i = 0;i < count;i++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; + } + } + else + { + static const int map[3] = { 0, 1, 3 }; + count = 3; + for(i = 0;i < count;i++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = map[i]; + } + } + device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = count; + } + + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + + avg_dist = 0.0f; + for(i = 0;i < conf->NumSpeakers;i++) + avg_dist += conf->Speakers[i].Distance; + avg_dist /= (ALfloat)conf->NumSpeakers; + InitNearFieldCtrl(device, avg_dist, + (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1, + (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d + ); + + InitDistanceComp(device, conf, speakermap); } -ALvoid aluInitPanning(ALCdevice *device) +static void InitHrtfPanning(ALCdevice *device) { - /* NOTE: These decoder coefficients are using FuMa channel ordering and - * normalization, since that's what was produced by the Ambisonic Decoder - * Toolbox. SetChannelMap will convert them to N3D. - */ - static const ChannelMap MonoCfg[1] = { - { FrontCenter, { 1.414213562f } }, - }, StereoCfg[2] = { - { FrontLeft, { 0.707106781f, 0.0f, 0.5f, 0.0f } }, - { FrontRight, { 0.707106781f, 0.0f, -0.5f, 0.0f } }, - }, QuadCfg[4] = { - { FrontLeft, { 0.353553f, 0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } }, - { FrontRight, { 0.353553f, 0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } }, - { BackLeft, { 0.353553f, -0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } }, - { BackRight, { 0.353553f, -0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } }, - }, X51SideCfg[5] = { - { FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } }, - { FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } }, - { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } }, - { SideLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } }, - { SideRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } }, - }, X51RearCfg[5] = { - { FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } }, - { FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } }, - { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } }, - { BackLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } }, - { BackRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } }, - }, X61Cfg[6] = { - { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } }, - { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } }, - { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } }, - { BackCenter, { 0.353556f, -0.461940f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.165723f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.000000f } }, - { SideLeft, { 0.289151f, -0.081301f, 0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, -0.032897f } }, - { SideRight, { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, 0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, 0.032897f } }, - }, X71Cfg[7] = { - { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } }, - { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } }, - { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } }, - { BackLeft, { 0.224752f, -0.295009f, 0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065799f } }, - { BackRight, { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f } }, - { SideLeft, { 0.224739f, 0.000000f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f } }, - { SideRight, { 0.224739f, 0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f } }, - }, BFormat3D[4] = { - { BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } }, - { BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } }, - { BFormatY, { 0.0f, 0.0f, 1.0f, 0.0f } }, - { BFormatZ, { 0.0f, 0.0f, 0.0f, 1.0f } }, + /* NOTE: azimuth goes clockwise. */ + static const struct AngularPoint AmbiPoints[] = { + { DEG2RAD( 90.0f), DEG2RAD( 0.0f) }, + { DEG2RAD( 35.2643897f), DEG2RAD( 45.0f) }, + { DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) }, + { DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) }, + { DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 0.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 45.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 90.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 135.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 180.0f) }, + { DEG2RAD( 0.0f), DEG2RAD(-135.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( -90.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( -45.0f) }, + { DEG2RAD(-35.2643897f), DEG2RAD( 45.0f) }, + { DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) }, + { DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) }, + { DEG2RAD(-35.2643897f), DEG2RAD( -45.0f) }, + { DEG2RAD(-90.0f), DEG2RAD( 0.0f) }, }; - const ChannelMap *chanmap = NULL; - ALfloat ambiscale = 1.0f; - size_t count = 0; + static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = { + { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f }, + { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f }, + { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f }, + }, AmbiMatrixHOA[][MAX_AMBI_COEFFS] = { + { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f }, + }; + static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = { + 3.00000000e+00f, 1.73205081e+00f + }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = { + 2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f + }; + static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 }; + static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 }; + const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA; + const ALfloat *restrict AmbiOrderHFGain = AmbiOrderHFGainFOA; + ALsizei count = 4; + ALsizei i; - device->AmbiScale = 1.0f; - memset(device->AmbiCoeffs, 0, sizeof(device->AmbiCoeffs)); - device->NumChannels = 0; + static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch"); + static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch"); - if(device->Hrtf) + if(device->AmbiUp) { - ALfloat (*coeffs_list[4])[2]; - ALuint *delay_list[4]; - ALuint i; + AmbiMatrix = AmbiMatrixHOA; + AmbiOrderHFGain = AmbiOrderHFGainHOA; + count = COUNTOF(IndexMap); + } - count = COUNTOF(BFormat3D); - chanmap = BFormat3D; - ambiscale = 1.0f; + device->Hrtf = al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count)); - for(i = 0;i < count;i++) - device->ChannelName[i] = chanmap[i].ChanName; - for(;i < MAX_OUTPUT_CHANNELS;i++) - device->ChannelName[i] = InvalidChannel; - SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE); + for(i = 0;i < count;i++) + { + device->Dry.Ambi.Map[i].Scale = 1.0f; + device->Dry.Ambi.Map[i].Index = IndexMap[i]; + } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; - for(i = 0;i < 4;++i) + if(device->AmbiUp) + { + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + for(i = 0;i < 4;i++) { - static const enum Channel inputs[4] = { BFormatW, BFormatX, BFormatY, BFormatZ }; - int chan = GetChannelIdxByName(device, inputs[i]); - coeffs_list[i] = device->Hrtf_Params[chan].Coeffs; - delay_list[i] = device->Hrtf_Params[chan].Delay; + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; } - GetBFormatHrtfCoeffs(device->Hrtf, 4, coeffs_list, delay_list); + device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = 4; - return; + ambiup_reset(device->AmbiUp, device, AmbiOrderHFGainFOA[0] / AmbiOrderHFGain[0], + AmbiOrderHFGainFOA[1] / AmbiOrderHFGain[1]); + } + else + { + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; } - if(LoadChannelSetup(device)) - return; + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); - switch(device->FmtChans) + BuildBFormatHrtf(device->HrtfHandle, + device->Hrtf, device->Dry.NumChannels, AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints), + AmbiOrderHFGain + ); + + InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1, + ChansPerOrder); +} + +static void InitUhjPanning(ALCdevice *device) +{ + ALsizei count = 3; + ALsizei i; + + for(i = 0;i < count;i++) { - case DevFmtMono: - count = COUNTOF(MonoCfg); - chanmap = MonoCfg; - ambiscale = ZERO_ORDER_SCALE; - break; + ALsizei acn = FuMa2ACN[i]; + device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn]; + device->Dry.Ambi.Map[i].Index = acn; + } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; - case DevFmtStereo: - count = COUNTOF(StereoCfg); - chanmap = StereoCfg; - ambiscale = FIRST_ORDER_SCALE; - break; + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; - case DevFmtQuad: - count = COUNTOF(QuadCfg); - chanmap = QuadCfg; - ambiscale = SECOND_ORDER_SCALE; - break; + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); +} - case DevFmtX51: - count = COUNTOF(X51SideCfg); - chanmap = X51SideCfg; - ambiscale = THIRD_ORDER_SCALE; - break; +void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq) +{ + /* Hold the HRTF the device last used, in case it's used again. */ + struct Hrtf *old_hrtf = device->HrtfHandle; + const char *mode; + bool headphones; + int bs2blevel; + size_t i; - case DevFmtX51Rear: - count = COUNTOF(X51RearCfg); - chanmap = X51RearCfg; - ambiscale = THIRD_ORDER_SCALE; - break; + al_free(device->Hrtf); + device->Hrtf = NULL; + device->HrtfHandle = NULL; + alstr_clear(&device->HrtfName); + device->Render_Mode = NormalRender; - case DevFmtX61: - count = COUNTOF(X61Cfg); - chanmap = X61Cfg; - ambiscale = THIRD_ORDER_SCALE; - break; + memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi)); + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = 0; + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + device->NumChannelsPerOrder[i] = 0; + + device->AvgSpeakerDist = 0.0f; + memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay)); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + } + + al_free(device->Stablizer); + device->Stablizer = NULL; + + if(device->FmtChans != DevFmtStereo) + { + ALsizei speakermap[MAX_OUTPUT_CHANNELS]; + const char *devname, *layout = NULL; + AmbDecConf conf, *pconf = NULL; + + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; + if(hrtf_appreq == Hrtf_Enable) + device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + + ambdec_init(&conf); + + devname = alstr_get_cstr(device->DeviceName); + switch(device->FmtChans) + { + case DevFmtQuad: layout = "quad"; break; + case DevFmtX51: /* fall-through */ + case DevFmtX51Rear: layout = "surround51"; break; + case DevFmtX61: layout = "surround61"; break; + case DevFmtX71: layout = "surround71"; break; + /* Mono, Stereo, and Ambisonics output don't use custom decoders. */ + case DevFmtMono: + case DevFmtStereo: + case DevFmtAmbi3D: + break; + } + if(layout) + { + const char *fname; + if(ConfigValueStr(devname, "decoder", layout, &fname)) + { + if(!ambdec_load(&conf, fname)) + ERR("Failed to load layout file %s\n", fname); + else + { + if(conf.ChanMask > 0xffff) + ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask); + else + { + if(MakeSpeakerMap(device, &conf, speakermap)) + pconf = &conf; + } + } + } + } + if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0)) + { + ambiup_free(&device->AmbiUp); + if(!device->AmbiDecoder) + device->AmbiDecoder = bformatdec_alloc(); + } + else + { + bformatdec_free(&device->AmbiDecoder); + if(device->FmtChans != DevFmtAmbi3D || device->AmbiOrder < 2) + ambiup_free(&device->AmbiUp); + else + { + if(!device->AmbiUp) + device->AmbiUp = ambiup_alloc(); + } + } + + if(!pconf) + InitPanning(device); + else if(device->AmbiDecoder) + InitHQPanning(device, pconf, speakermap); + else + InitCustomPanning(device, pconf, speakermap); + + /* Enable the stablizer only for formats that have front-left, front- + * right, and front-center outputs. + */ + switch(device->FmtChans) + { + case DevFmtX51: + case DevFmtX51Rear: + case DevFmtX61: case DevFmtX71: - count = COUNTOF(X71Cfg); - chanmap = X71Cfg; - ambiscale = THIRD_ORDER_SCALE; - break; + if(GetConfigValueBool(devname, NULL, "front-stablizer", 0)) + { + /* Initialize band-splitting filters for the front-left and + * front-right channels, with a crossover at 5khz (could be + * higher). + */ + ALfloat scale = (ALfloat)(5000.0 / device->Frequency); + FrontStablizer *stablizer = al_calloc(16, sizeof(*stablizer)); - case DevFmtBFormat3D: - count = COUNTOF(BFormat3D); - chanmap = BFormat3D; - ambiscale = 1.0f; + bandsplit_init(&stablizer->LFilter, scale); + stablizer->RFilter = stablizer->LFilter; + + /* Initialize all-pass filters for all other channels. */ + splitterap_init(&stablizer->APFilter[0], scale); + for(i = 1;i < (size_t)device->RealOut.NumChannels;i++) + stablizer->APFilter[i] = stablizer->APFilter[0]; + + device->Stablizer = stablizer; + } + break; + case DevFmtMono: + case DevFmtStereo: + case DevFmtQuad: + case DevFmtAmbi3D: break; + } + TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled"); + + ambdec_deinit(&conf); + return; + } + + bformatdec_free(&device->AmbiDecoder); + + headphones = device->IsHeadphones; + if(device->Type != Loopback) + { + const char *mode; + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode)) + { + if(strcasecmp(mode, "headphones") == 0) + headphones = true; + else if(strcasecmp(mode, "speakers") == 0) + headphones = false; + else if(strcasecmp(mode, "auto") != 0) + ERR("Unexpected stereo-mode: %s\n", mode); + } + } + + if(hrtf_userreq == Hrtf_Default) + { + bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) || + (hrtf_appreq == Hrtf_Enable); + if(!usehrtf) goto no_hrtf; + + device->HrtfStatus = ALC_HRTF_ENABLED_SOFT; + if(headphones && hrtf_appreq != Hrtf_Disable) + device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT; + } + else + { + if(hrtf_userreq != Hrtf_Enable) + { + if(hrtf_appreq == Hrtf_Enable) + device->HrtfStatus = ALC_HRTF_DENIED_SOFT; + goto no_hrtf; + } + device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT; + } + + if(VECTOR_SIZE(device->HrtfList) == 0) + { + VECTOR_DEINIT(device->HrtfList); + device->HrtfList = EnumerateHrtf(device->DeviceName); } - SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE); + if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList)) + { + const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, hrtf_id); + struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf); + if(hrtf && hrtf->sampleRate == device->Frequency) + { + device->HrtfHandle = hrtf; + alstr_copy(&device->HrtfName, entry->name); + } + else if(hrtf) + Hrtf_DecRef(hrtf); + } + + for(i = 0;!device->HrtfHandle && i < VECTOR_SIZE(device->HrtfList);i++) + { + const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, i); + struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf); + if(hrtf && hrtf->sampleRate == device->Frequency) + { + device->HrtfHandle = hrtf; + alstr_copy(&device->HrtfName, entry->name); + } + else if(hrtf) + Hrtf_DecRef(hrtf); + } + + if(device->HrtfHandle) + { + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; + + device->Render_Mode = HrtfRender; + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode)) + { + if(strcasecmp(mode, "full") == 0) + device->Render_Mode = HrtfRender; + else if(strcasecmp(mode, "basic") == 0) + device->Render_Mode = NormalRender; + else + ERR("Unexpected hrtf-mode: %s\n", mode); + } + + if(device->Render_Mode == HrtfRender) + { + /* Don't bother with HOA when using full HRTF rendering. Nothing + * needs it, and it eases the CPU/memory load. + */ + ambiup_free(&device->AmbiUp); + } + else + { + if(!device->AmbiUp) + device->AmbiUp = ambiup_alloc(); + } + + TRACE("%s HRTF rendering enabled, using \"%s\"\n", + ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"), + alstr_get_cstr(device->HrtfName) + ); + InitHrtfPanning(device); + return; + } + device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + +no_hrtf: + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; + TRACE("HRTF disabled\n"); + + device->Render_Mode = StereoPair; + + ambiup_free(&device->AmbiUp); + + bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) || + (hrtf_appreq == Hrtf_Enable)) ? 5 : 0; + if(device->Type != Loopback) + ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel); + if(bs2blevel > 0 && bs2blevel <= 6) + { + device->Bs2b = al_calloc(16, sizeof(*device->Bs2b)); + bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency); + TRACE("BS2B enabled\n"); + InitPanning(device); + return; + } + + TRACE("BS2B disabled\n"); + + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-encoding", &mode)) + { + if(strcasecmp(mode, "uhj") == 0) + device->Render_Mode = NormalRender; + else if(strcasecmp(mode, "panpot") != 0) + ERR("Unexpected stereo-encoding: %s\n", mode); + } + if(device->Render_Mode == NormalRender) + { + device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder)); + TRACE("UHJ enabled\n"); + InitUhjPanning(device); + return; + } + + TRACE("UHJ disabled\n"); + InitPanning(device); +} + + +void aluInitEffectPanning(ALeffectslot *slot) +{ + ALsizei i; + + memset(slot->ChanMap, 0, sizeof(slot->ChanMap)); + slot->NumChannels = 0; + + for(i = 0;i < MAX_EFFECT_CHANNELS;i++) + { + slot->ChanMap[i].Scale = 1.0f; + slot->ChanMap[i].Index = i; + } + slot->NumChannels = i; } |