2016-03-15 05:08:05 -07:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "bformatdec.h"
|
|
|
|
#include "ambdec.h"
|
2016-05-31 10:18:34 -07:00
|
|
|
#include "mixer_defs.h"
|
2016-03-15 05:08:05 -07:00
|
|
|
#include "alu.h"
|
|
|
|
|
2016-03-23 12:53:36 -07:00
|
|
|
#include "threads.h"
|
2016-03-29 00:44:58 -07:00
|
|
|
#include "almalloc.h"
|
2016-03-23 12:53:36 -07:00
|
|
|
|
2016-03-15 05:08:05 -07:00
|
|
|
|
2016-08-21 03:05:42 -07:00
|
|
|
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-08-21 03:05:42 -07:00
|
|
|
void bandsplit_clear(BandSplitter *splitter)
|
|
|
|
{
|
|
|
|
splitter->lp_z1 = 0.0f;
|
|
|
|
splitter->lp_z2 = 0.0f;
|
|
|
|
splitter->hp_z1 = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
|
|
|
const ALfloat *input, ALuint count)
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
ALfloat coeff, d, x;
|
2016-05-31 07:50:23 -07:00
|
|
|
ALfloat z1, z2;
|
2016-03-15 05:08:05 -07:00
|
|
|
ALuint i;
|
|
|
|
|
|
|
|
coeff = splitter->coeff*0.5f + 0.5f;
|
2016-05-31 07:50:23 -07:00
|
|
|
z1 = splitter->lp_z1;
|
|
|
|
z2 = splitter->lp_z2;
|
2016-03-15 05:08:05 -07:00
|
|
|
for(i = 0;i < count;i++)
|
|
|
|
{
|
|
|
|
x = input[i];
|
|
|
|
|
2016-05-31 07:50:23 -07:00
|
|
|
d = (x - z1) * coeff;
|
|
|
|
x = z1 + d;
|
|
|
|
z1 = x + d;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
2016-05-31 07:50:23 -07:00
|
|
|
d = (x - z2) * coeff;
|
|
|
|
x = z2 + d;
|
|
|
|
z2 = x + d;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
|
|
|
lpout[i] = x;
|
|
|
|
}
|
2016-05-31 07:50:23 -07:00
|
|
|
splitter->lp_z1 = z1;
|
|
|
|
splitter->lp_z2 = z2;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
|
|
|
coeff = splitter->coeff;
|
2016-05-31 07:50:23 -07:00
|
|
|
z1 = splitter->hp_z1;
|
2016-03-15 05:08:05 -07:00
|
|
|
for(i = 0;i < count;i++)
|
|
|
|
{
|
|
|
|
x = input[i];
|
|
|
|
|
2016-05-31 07:50:23 -07:00
|
|
|
d = x - coeff*z1;
|
|
|
|
x = z1 + coeff*d;
|
|
|
|
z1 = d;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
|
|
|
hpout[i] = x - lpout[i];
|
|
|
|
}
|
2016-05-31 07:50:23 -07:00
|
|
|
splitter->hp_z1 = z1;
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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) */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-07-17 00:29:02 -07:00
|
|
|
enum FreqBand {
|
|
|
|
FB_HighFreq,
|
|
|
|
FB_LowFreq,
|
|
|
|
FB_Max
|
2016-03-23 12:53:36 -07:00
|
|
|
};
|
2016-07-17 00:29:02 -07:00
|
|
|
|
2016-09-06 03:10:38 -07:00
|
|
|
/* These points are in AL coordinates! */
|
|
|
|
static const ALfloat Ambi2DPoints[4][3] = {
|
2016-08-30 04:21:57 -07:00
|
|
|
{ -0.707106781f, 0.0f, -0.707106781f },
|
|
|
|
{ 0.707106781f, 0.0f, -0.707106781f },
|
|
|
|
{ -0.707106781f, 0.0f, 0.707106781f },
|
|
|
|
{ 0.707106781f, 0.0f, 0.707106781f },
|
|
|
|
};
|
2016-09-06 03:10:38 -07:00
|
|
|
static const ALfloat Ambi2DDecoder[4][FB_Max][MAX_AMBI_COEFFS] = {
|
2016-07-17 00:29:02 -07:00
|
|
|
{ { 0.353553f, 0.204094f, 0.0f, 0.204094f }, { 0.25f, 0.204094f, 0.0f, 0.204094f } },
|
|
|
|
{ { 0.353553f, -0.204094f, 0.0f, 0.204094f }, { 0.25f, -0.204094f, 0.0f, 0.204094f } },
|
|
|
|
{ { 0.353553f, 0.204094f, 0.0f, -0.204094f }, { 0.25f, 0.204094f, 0.0f, -0.204094f } },
|
|
|
|
{ { 0.353553f, -0.204094f, 0.0f, -0.204094f }, { 0.25f, -0.204094f, 0.0f, -0.204094f } },
|
2016-04-09 08:04:56 -07:00
|
|
|
};
|
2016-11-24 21:29:53 -08:00
|
|
|
static ALfloat Ambi2DEncoderT[4][MAX_AMBI_COEFFS];
|
2016-03-23 12:53:36 -07:00
|
|
|
|
2016-09-06 03:10:38 -07:00
|
|
|
/* These points are in AL coordinates! */
|
|
|
|
static const ALfloat Ambi3DPoints[8][3] = {
|
2016-07-30 09:29:21 -07:00
|
|
|
{ -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 },
|
|
|
|
};
|
2016-09-06 03:10:38 -07:00
|
|
|
static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
|
2016-09-05 10:33:52 -07:00
|
|
|
{ { 0.25f, 0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, 0.125f, 0.125f } },
|
|
|
|
{ { 0.25f, -0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, 0.125f, 0.125f } },
|
|
|
|
{ { 0.25f, 0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, 0.125f, -0.125f } },
|
|
|
|
{ { 0.25f, -0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, 0.125f, -0.125f } },
|
|
|
|
{ { 0.25f, 0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, -0.125f, 0.125f } },
|
|
|
|
{ { 0.25f, -0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, -0.125f, 0.125f } },
|
|
|
|
{ { 0.25f, 0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, -0.125f, -0.125f } },
|
|
|
|
{ { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
|
2016-04-09 08:04:56 -07:00
|
|
|
};
|
2016-11-24 21:29:53 -08:00
|
|
|
static ALfloat Ambi3DEncoderT[8][MAX_AMBI_COEFFS];
|
2016-03-23 12:53:36 -07:00
|
|
|
|
|
|
|
|
2016-09-02 00:29:46 -07:00
|
|
|
static RowMixerFunc MixMatrixRow = MixRow_C;
|
2016-05-31 10:18:34 -07:00
|
|
|
|
|
|
|
|
|
|
|
static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
|
|
|
|
|
|
|
|
static void init_bformatdec(void)
|
2016-03-23 12:53:36 -07:00
|
|
|
{
|
2016-03-25 19:57:25 -07:00
|
|
|
ALuint i, j;
|
|
|
|
|
2016-09-06 13:21:11 -07:00
|
|
|
MixMatrixRow = SelectRowMixer();
|
2016-05-31 10:18:34 -07:00
|
|
|
|
2016-09-06 03:10:38 -07:00
|
|
|
for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
|
2016-11-24 21:29:53 -08:00
|
|
|
CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, Ambi3DEncoderT[i]);
|
2016-04-24 21:42:59 -07:00
|
|
|
|
2016-09-06 03:10:38 -07:00
|
|
|
for(i = 0;i < COUNTOF(Ambi2DPoints);i++)
|
2016-03-25 19:57:25 -07:00
|
|
|
{
|
2016-11-24 21:29:53 -08:00
|
|
|
CalcDirectionCoeffs(Ambi2DPoints[i], 0.0f, Ambi2DEncoderT[i]);
|
2016-09-06 03:10:38 -07:00
|
|
|
|
2016-03-25 19:57:25 -07:00
|
|
|
/* Remove the skipped height-related coefficients for 2D rendering. */
|
2016-11-24 21:29:53 -08:00
|
|
|
Ambi2DEncoderT[i][2] = Ambi2DEncoderT[i][3];
|
|
|
|
Ambi2DEncoderT[i][3] = Ambi2DEncoderT[i][4];
|
|
|
|
Ambi2DEncoderT[i][4] = Ambi2DEncoderT[i][8];
|
|
|
|
Ambi2DEncoderT[i][5] = Ambi2DEncoderT[i][9];
|
|
|
|
Ambi2DEncoderT[i][6] = Ambi2DEncoderT[i][15];
|
2016-03-25 19:57:25 -07:00
|
|
|
for(j = 7;j < MAX_AMBI_COEFFS;j++)
|
2016-11-24 21:29:53 -08:00
|
|
|
Ambi2DEncoderT[i][j] = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This typedef is needed for SAFE_CONST to work. */
|
|
|
|
typedef ALfloat ALfloatMAX_AMBI_COEFFS[MAX_AMBI_COEFFS];
|
|
|
|
|
|
|
|
static void GenUpsamplerGains(const ALfloat (*restrict EncoderT)[MAX_AMBI_COEFFS],
|
|
|
|
const ALfloat (*restrict Decoder)[FB_Max][MAX_AMBI_COEFFS],
|
|
|
|
ALsizei InChannels,
|
|
|
|
ALfloat (*restrict OutGains)[MAX_OUTPUT_CHANNELS][FB_Max],
|
|
|
|
ALsizei OutChannels)
|
|
|
|
{
|
|
|
|
ALsizei i, j, k;
|
|
|
|
|
|
|
|
/* Combine the matrices that do the in->virt and virt->out conversions so
|
|
|
|
* we get a single in->out conversion. NOTE: the Encoder matrix and output
|
|
|
|
* are transposed, so the input channels line up with the rows and the
|
|
|
|
* output channels line up with the columns.
|
|
|
|
*/
|
|
|
|
for(i = 0;i < 4;i++)
|
|
|
|
{
|
|
|
|
for(j = 0;j < OutChannels;j++)
|
|
|
|
{
|
|
|
|
ALfloat hfgain=0.0f, lfgain=0.0f;
|
|
|
|
for(k = 0;k < InChannels;k++)
|
|
|
|
{
|
|
|
|
hfgain += Decoder[k][FB_HighFreq][i]*EncoderT[k][j];
|
|
|
|
lfgain += Decoder[k][FB_LowFreq][i]*EncoderT[k][j];
|
|
|
|
}
|
|
|
|
OutGains[i][j][FB_HighFreq] = hfgain;
|
|
|
|
OutGains[i][j][FB_LowFreq] = lfgain;
|
|
|
|
}
|
2016-03-25 19:57:25 -07:00
|
|
|
}
|
2016-03-23 12:53:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-26 17:33:49 -07:00
|
|
|
#define MAX_DELAY_LENGTH 128
|
|
|
|
|
2016-07-17 00:29:02 -07:00
|
|
|
/* NOTE: BandSplitter filters are unused with single-band decoding */
|
2016-03-15 05:08:05 -07:00
|
|
|
typedef struct BFormatDec {
|
2016-03-28 14:50:18 -07:00
|
|
|
ALboolean Enabled[MAX_OUTPUT_CHANNELS];
|
|
|
|
|
2016-07-17 00:29:02 -07:00
|
|
|
union {
|
|
|
|
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
|
|
|
|
alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
|
|
|
} Matrix;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
|
|
|
BandSplitter XOver[MAX_AMBI_COEFFS];
|
|
|
|
|
|
|
|
ALfloat (*Samples)[BUFFERSIZE];
|
|
|
|
/* These two alias into Samples */
|
|
|
|
ALfloat (*SamplesHF)[BUFFERSIZE];
|
|
|
|
ALfloat (*SamplesLF)[BUFFERSIZE];
|
|
|
|
|
2016-03-27 13:23:37 -07:00
|
|
|
alignas(16) ALfloat ChannelMix[BUFFERSIZE];
|
2016-03-23 12:53:36 -07:00
|
|
|
|
2016-03-26 17:33:49 -07:00
|
|
|
struct {
|
|
|
|
alignas(16) ALfloat Buffer[MAX_DELAY_LENGTH];
|
|
|
|
ALuint Length; /* Valid range is [0...MAX_DELAY_LENGTH). */
|
|
|
|
} Delay[MAX_OUTPUT_CHANNELS];
|
|
|
|
|
2016-03-27 13:23:37 -07:00
|
|
|
struct {
|
2016-04-09 08:04:56 -07:00
|
|
|
BandSplitter XOver[4];
|
|
|
|
|
2016-08-30 04:21:57 -07:00
|
|
|
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
|
2016-03-27 13:23:37 -07:00
|
|
|
} UpSampler;
|
2016-03-26 17:33:49 -07:00
|
|
|
|
2016-03-15 05:08:05 -07:00
|
|
|
ALuint NumChannels;
|
|
|
|
ALboolean DualBand;
|
2016-03-25 19:57:25 -07:00
|
|
|
ALboolean Periphonic;
|
2016-03-15 05:08:05 -07:00
|
|
|
} BFormatDec;
|
|
|
|
|
|
|
|
BFormatDec *bformatdec_alloc()
|
|
|
|
{
|
2016-05-31 10:18:34 -07:00
|
|
|
alcall_once(&bformatdec_inited, init_bformatdec);
|
2016-03-15 05:08:05 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 17:52:20 -07:00
|
|
|
int bformatdec_getOrder(const struct BFormatDec *dec)
|
|
|
|
{
|
2016-03-25 19:57:25 -07:00
|
|
|
if(dec->Periphonic)
|
|
|
|
{
|
|
|
|
if(dec->NumChannels > 9) return 3;
|
|
|
|
if(dec->NumChannels > 4) return 2;
|
|
|
|
if(dec->NumChannels > 1) return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(dec->NumChannels > 5) return 3;
|
|
|
|
if(dec->NumChannels > 3) return 2;
|
|
|
|
if(dec->NumChannels > 1) return 1;
|
|
|
|
}
|
2016-03-22 17:52:20 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-26 17:52:42 -07:00
|
|
|
void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS], int flags)
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
2016-07-17 00:46:18 -07:00
|
|
|
static const ALuint map2DTo3D[MAX_AMBI2D_COEFFS] = {
|
2016-03-25 19:57:25 -07:00
|
|
|
0, 1, 3, 4, 8, 9, 15
|
|
|
|
};
|
2016-03-15 05:08:05 -07:00
|
|
|
const ALfloat *coeff_scale = UnitScale;
|
2016-03-26 18:06:36 -07:00
|
|
|
ALfloat distgain[MAX_OUTPUT_CHANNELS];
|
2016-03-26 17:33:49 -07:00
|
|
|
ALfloat maxdist, ratio;
|
2016-11-24 21:29:53 -08:00
|
|
|
ALuint i;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
|
|
|
al_free(dec->Samples);
|
|
|
|
dec->Samples = NULL;
|
|
|
|
dec->SamplesHF = NULL;
|
|
|
|
dec->SamplesLF = NULL;
|
|
|
|
|
|
|
|
dec->NumChannels = chancount;
|
2016-04-09 08:04:56 -07:00
|
|
|
dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
|
2016-03-15 05:08:05 -07:00
|
|
|
dec->SamplesHF = dec->Samples;
|
|
|
|
dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
|
|
|
|
|
2016-03-28 14:50:18 -07:00
|
|
|
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
|
|
|
dec->Enabled[i] = AL_FALSE;
|
|
|
|
for(i = 0;i < conf->NumSpeakers;i++)
|
|
|
|
dec->Enabled[chanmap[i]] = AL_TRUE;
|
|
|
|
|
2016-03-15 05:08:05 -07:00
|
|
|
if(conf->CoeffScale == ADS_SN3D)
|
|
|
|
coeff_scale = SN3D2N3DScale;
|
|
|
|
else if(conf->CoeffScale == ADS_FuMa)
|
|
|
|
coeff_scale = FuMa2N3DScale;
|
|
|
|
|
2016-04-09 08:04:56 -07:00
|
|
|
ratio = 400.0f / (ALfloat)srate;
|
|
|
|
for(i = 0;i < 4;i++)
|
|
|
|
bandsplit_init(&dec->UpSampler.XOver[i], ratio);
|
2016-08-30 04:21:57 -07:00
|
|
|
memset(dec->UpSampler.Gains, 0, sizeof(dec->UpSampler.Gains));
|
2016-06-01 05:30:06 -07:00
|
|
|
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
2016-03-23 12:53:36 -07:00
|
|
|
{
|
2016-11-24 21:29:53 -08:00
|
|
|
GenUpsamplerGains(SAFE_CONST(ALfloatMAX_AMBI_COEFFS*,Ambi3DEncoderT),
|
|
|
|
Ambi3DDecoder, COUNTOF(Ambi3DDecoder),
|
|
|
|
dec->UpSampler.Gains, dec->NumChannels);
|
2016-03-25 19:57:25 -07:00
|
|
|
dec->Periphonic = AL_TRUE;
|
2016-03-23 12:53:36 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-24 21:29:53 -08:00
|
|
|
GenUpsamplerGains(SAFE_CONST(ALfloatMAX_AMBI_COEFFS*,Ambi2DEncoderT),
|
|
|
|
Ambi2DDecoder, COUNTOF(Ambi2DDecoder),
|
|
|
|
dec->UpSampler.Gains, dec->NumChannels);
|
2016-03-25 19:57:25 -07:00
|
|
|
dec->Periphonic = AL_FALSE;
|
2016-03-23 12:53:36 -07:00
|
|
|
}
|
|
|
|
|
2016-03-26 18:06:36 -07:00
|
|
|
maxdist = 0.0f;
|
|
|
|
for(i = 0;i < conf->NumSpeakers;i++)
|
|
|
|
{
|
|
|
|
maxdist = maxf(maxdist, conf->Speakers[i].Distance);
|
|
|
|
distgain[i] = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(dec->Delay, 0, sizeof(dec->Delay));
|
|
|
|
if((flags&BFDF_DistanceComp) && maxdist > 0.0f)
|
|
|
|
{
|
|
|
|
for(i = 0;i < conf->NumSpeakers;i++)
|
|
|
|
{
|
|
|
|
ALuint chan = chanmap[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 *
|
|
|
|
(ALfloat)srate + 0.5f);
|
|
|
|
if(delay >= (ALfloat)MAX_DELAY_LENGTH)
|
|
|
|
ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n",
|
|
|
|
al_string_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH);
|
|
|
|
|
|
|
|
dec->Delay[chan].Length = (ALuint)clampf(delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1));
|
|
|
|
distgain[i] = conf->Speakers[i].Distance / maxdist;
|
|
|
|
TRACE("Channel %u \"%s\" distance compensation: %u samples, %f gain\n", chan,
|
|
|
|
al_string_get_cstr(conf->Speakers[i].Name), dec->Delay[chan].Length, distgain[i]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-17 00:29:02 -07:00
|
|
|
memset(&dec->Matrix, 0, sizeof(dec->Matrix));
|
2016-03-15 05:08:05 -07:00
|
|
|
if(conf->FreqBands == 1)
|
|
|
|
{
|
|
|
|
dec->DualBand = AL_FALSE;
|
2016-07-17 00:29:02 -07:00
|
|
|
for(i = 0;i < conf->NumSpeakers;i++)
|
|
|
|
{
|
|
|
|
ALuint chan = chanmap[i];
|
|
|
|
ALfloat gain;
|
|
|
|
ALuint j, k;
|
|
|
|
|
|
|
|
if(!dec->Periphonic)
|
|
|
|
{
|
2016-07-17 00:46:18 -07:00
|
|
|
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
2016-07-17 00:29:02 -07:00
|
|
|
{
|
|
|
|
ALuint l = map2DTo3D[j];
|
|
|
|
if(j == 0) gain = conf->HFOrderGain[0];
|
|
|
|
else if(j == 1) gain = conf->HFOrderGain[1];
|
|
|
|
else if(j == 3) gain = conf->HFOrderGain[2];
|
|
|
|
else if(j == 5) gain = conf->HFOrderGain[3];
|
|
|
|
if((conf->ChanMask&(1<<l)))
|
|
|
|
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
|
|
|
|
gain * distgain[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
|
|
|
{
|
|
|
|
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)))
|
|
|
|
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
|
|
|
|
gain * distgain[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
for(i = 0;i < conf->NumSpeakers;i++)
|
|
|
|
{
|
|
|
|
ALuint chan = chanmap[i];
|
2016-03-22 14:42:53 -07:00
|
|
|
ALfloat gain;
|
2016-07-17 00:29:02 -07:00
|
|
|
ALuint j, k;
|
2016-03-15 05:08:05 -07:00
|
|
|
|
2016-03-25 19:57:25 -07:00
|
|
|
if(!dec->Periphonic)
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
2016-07-17 00:46:18 -07:00
|
|
|
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
2016-07-17 00:29:02 -07:00
|
|
|
{
|
|
|
|
ALuint l = map2DTo3D[j];
|
|
|
|
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
|
|
|
|
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
|
|
|
|
else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
|
|
|
|
else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
|
|
|
|
if((conf->ChanMask&(1<<l)))
|
|
|
|
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
|
|
|
|
coeff_scale[l] * gain *
|
|
|
|
distgain[i];
|
|
|
|
}
|
2016-07-17 00:46:18 -07:00
|
|
|
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
2016-03-25 19:57:25 -07:00
|
|
|
{
|
|
|
|
ALuint l = map2DTo3D[j];
|
|
|
|
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
|
|
|
|
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
|
|
|
|
else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
|
|
|
|
else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
|
|
|
|
if((conf->ChanMask&(1<<l)))
|
2016-07-17 00:29:02 -07:00
|
|
|
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
|
|
|
|
coeff_scale[l] * gain *
|
|
|
|
distgain[i];
|
2016-03-25 19:57:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-07-17 00:29:02 -07:00
|
|
|
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
|
|
|
{
|
|
|
|
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
|
|
|
|
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
|
|
|
|
else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
|
|
|
|
else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
|
|
|
|
if((conf->ChanMask&(1<<j)))
|
|
|
|
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
|
|
|
|
coeff_scale[j] * gain *
|
|
|
|
distgain[i];
|
|
|
|
}
|
|
|
|
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
2016-03-25 19:57:25 -07:00
|
|
|
{
|
|
|
|
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
|
|
|
|
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
|
|
|
|
else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
|
|
|
|
else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
|
|
|
|
if((conf->ChanMask&(1<<j)))
|
2016-07-17 00:29:02 -07:00
|
|
|
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
|
|
|
|
coeff_scale[j] * gain *
|
|
|
|
distgain[i];
|
2016-03-25 19:57:25 -07:00
|
|
|
}
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-04 16:25:43 -07:00
|
|
|
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
|
2016-03-15 05:08:05 -07:00
|
|
|
{
|
|
|
|
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++)
|
|
|
|
{
|
2016-03-28 14:50:18 -07:00
|
|
|
if(!dec->Enabled[chan])
|
|
|
|
continue;
|
|
|
|
|
2016-03-26 17:33:49 -07:00
|
|
|
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
2016-07-17 00:29:02 -07:00
|
|
|
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
|
2016-10-04 16:25:43 -07:00
|
|
|
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
|
|
|
|
SamplesToDo
|
2016-07-17 00:29:02 -07:00
|
|
|
);
|
|
|
|
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
|
2016-10-04 16:25:43 -07:00
|
|
|
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0,
|
|
|
|
SamplesToDo
|
2016-07-17 00:29:02 -07:00
|
|
|
);
|
2016-03-26 17:33:49 -07:00
|
|
|
|
|
|
|
if(dec->Delay[chan].Length > 0)
|
|
|
|
{
|
|
|
|
const ALuint base = dec->Delay[chan].Length;
|
|
|
|
if(SamplesToDo >= base)
|
|
|
|
{
|
|
|
|
for(i = 0;i < base;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
|
2016-03-26 17:33:49 -07:00
|
|
|
for(;i < SamplesToDo;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->ChannelMix[i-base];
|
2016-03-26 17:33:49 -07:00
|
|
|
memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base],
|
2016-03-26 18:06:36 -07:00
|
|
|
base*sizeof(ALfloat));
|
2016-03-26 17:33:49 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(i = 0;i < SamplesToDo;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
|
2016-03-26 17:33:49 -07:00
|
|
|
memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo,
|
|
|
|
base - SamplesToDo);
|
|
|
|
memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix,
|
2016-03-26 18:06:36 -07:00
|
|
|
SamplesToDo*sizeof(ALfloat));
|
2016-03-26 17:33:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else for(i = 0;i < SamplesToDo;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->ChannelMix[i];
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(chan = 0;chan < OutChannels;chan++)
|
2016-03-26 17:33:49 -07:00
|
|
|
{
|
2016-03-28 14:50:18 -07:00
|
|
|
if(!dec->Enabled[chan])
|
|
|
|
continue;
|
|
|
|
|
2016-03-26 17:33:49 -07:00
|
|
|
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
2016-07-17 00:29:02 -07:00
|
|
|
MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
|
2016-10-04 16:25:43 -07:00
|
|
|
dec->NumChannels, 0, SamplesToDo);
|
2016-03-26 17:33:49 -07:00
|
|
|
|
|
|
|
if(dec->Delay[chan].Length > 0)
|
|
|
|
{
|
|
|
|
const ALuint base = dec->Delay[chan].Length;
|
|
|
|
if(SamplesToDo >= base)
|
|
|
|
{
|
|
|
|
for(i = 0;i < base;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
|
2016-03-26 17:33:49 -07:00
|
|
|
for(;i < SamplesToDo;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->ChannelMix[i-base];
|
2016-03-26 17:33:49 -07:00
|
|
|
memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base],
|
2016-03-26 18:06:36 -07:00
|
|
|
base*sizeof(ALfloat));
|
2016-03-26 17:33:49 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(i = 0;i < SamplesToDo;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
|
2016-03-26 17:33:49 -07:00
|
|
|
memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo,
|
|
|
|
base - SamplesToDo);
|
|
|
|
memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix,
|
2016-03-26 18:06:36 -07:00
|
|
|
SamplesToDo*sizeof(ALfloat));
|
2016-03-26 17:33:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else for(i = 0;i < SamplesToDo;i++)
|
2016-03-26 18:06:36 -07:00
|
|
|
OutBuffer[chan][i] += dec->ChannelMix[i];
|
2016-03-26 17:33:49 -07:00
|
|
|
}
|
2016-03-15 05:08:05 -07:00
|
|
|
}
|
|
|
|
}
|
2016-03-23 12:53:36 -07:00
|
|
|
|
|
|
|
|
2016-10-04 16:25:43 -07:00
|
|
|
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo)
|
2016-03-23 12:53:36 -07:00
|
|
|
{
|
2016-09-01 07:08:52 -07:00
|
|
|
ALuint i, j;
|
2016-04-09 08:04:56 -07:00
|
|
|
|
2016-03-23 12:53:36 -07:00
|
|
|
/* This up-sampler is very simplistic. It essentially decodes the first-
|
|
|
|
* order content to a square channel array (or cube if height is desired),
|
2016-08-30 04:21:57 -07:00
|
|
|
* then encodes those points onto the higher order soundfield. The decoder
|
|
|
|
* and encoder matrices have been combined to directly convert each input
|
|
|
|
* channel to the output, without the need for storing the virtual channel
|
|
|
|
* array.
|
2016-03-23 12:53:36 -07:00
|
|
|
*/
|
2016-09-01 07:08:52 -07:00
|
|
|
for(i = 0;i < InChannels;i++)
|
2016-03-23 12:53:36 -07:00
|
|
|
{
|
2016-08-30 04:21:57 -07:00
|
|
|
/* First, split the first-order components into low and high frequency
|
|
|
|
* bands.
|
|
|
|
*/
|
2016-09-01 07:08:52 -07:00
|
|
|
bandsplit_process(&dec->UpSampler.XOver[i],
|
2016-08-30 04:21:57 -07:00
|
|
|
dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
|
2016-09-01 07:08:52 -07:00
|
|
|
InSamples[i], SamplesToDo
|
2016-08-30 04:21:57 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/* Now write each band to the output. */
|
2016-03-23 12:53:36 -07:00
|
|
|
for(j = 0;j < dec->NumChannels;j++)
|
2016-09-01 07:08:52 -07:00
|
|
|
MixMatrixRow(OutBuffer[j], dec->UpSampler.Gains[i][j],
|
2016-10-04 16:25:43 -07:00
|
|
|
SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
|
|
|
|
SamplesToDo
|
|
|
|
);
|
2016-03-23 12:53:36 -07:00
|
|
|
}
|
|
|
|
}
|
2016-07-30 09:29:21 -07:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct AmbiUpsampler {
|
2016-08-30 04:21:57 -07:00
|
|
|
alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
|
2016-07-30 09:29:21 -07:00
|
|
|
|
|
|
|
BandSplitter XOver[4];
|
|
|
|
|
2016-08-30 04:21:57 -07:00
|
|
|
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
|
2016-07-30 09:29:21 -07:00
|
|
|
} 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)
|
|
|
|
{
|
2016-08-30 04:21:57 -07:00
|
|
|
ALfloat gains[8][MAX_OUTPUT_CHANNELS];
|
2016-07-30 09:29:21 -07:00
|
|
|
ALfloat ratio;
|
2016-11-24 21:29:53 -08:00
|
|
|
ALuint i;
|
2016-07-30 09:29:21 -07:00
|
|
|
|
|
|
|
ratio = 400.0f / (ALfloat)device->Frequency;
|
|
|
|
for(i = 0;i < 4;i++)
|
|
|
|
bandsplit_init(&ambiup->XOver[i], ratio);
|
|
|
|
|
2016-11-24 21:29:53 -08:00
|
|
|
for(i = 0;i < COUNTOF(Ambi3DEncoderT);i++)
|
|
|
|
ComputePanningGains(device->Dry, Ambi3DEncoderT[i], 1.0f, gains[i]);
|
2016-08-30 04:21:57 -07:00
|
|
|
|
|
|
|
memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
|
2016-11-24 21:29:53 -08:00
|
|
|
GenUpsamplerGains(SAFE_CONST(ALfloatMAX_AMBI_COEFFS*,gains),
|
|
|
|
Ambi3DDecoder, COUNTOF(Ambi3DDecoder),
|
|
|
|
ambiup->Gains, device->Dry.NumChannels);
|
2016-07-30 09:29:21 -07:00
|
|
|
}
|
|
|
|
|
2016-10-04 16:25:43 -07:00
|
|
|
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
|
2016-07-30 09:29:21 -07:00
|
|
|
{
|
2016-09-01 07:08:52 -07:00
|
|
|
ALuint i, j;
|
2016-07-30 09:29:21 -07:00
|
|
|
|
2016-09-01 07:08:52 -07:00
|
|
|
for(i = 0;i < 4;i++)
|
2016-07-30 09:29:21 -07:00
|
|
|
{
|
2016-09-01 07:08:52 -07:00
|
|
|
bandsplit_process(&ambiup->XOver[i],
|
2016-08-30 04:21:57 -07:00
|
|
|
ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
|
2016-09-01 07:08:52 -07:00
|
|
|
InSamples[i], SamplesToDo
|
2016-08-30 04:21:57 -07:00
|
|
|
);
|
2016-07-30 09:29:21 -07:00
|
|
|
|
|
|
|
for(j = 0;j < OutChannels;j++)
|
2016-09-01 07:08:52 -07:00
|
|
|
MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
|
2016-10-04 16:25:43 -07:00
|
|
|
SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
|
|
|
|
SamplesToDo
|
|
|
|
);
|
2016-07-30 09:29:21 -07:00
|
|
|
}
|
|
|
|
}
|