Index: main/libaf/af_hrtf.c =================================================================== RCS file: /cvsroot/mplayer/main/libaf/af_hrtf.c,v retrieving revision 1.5 diff -u -r1.5 af_hrtf.c --- main/libaf/af_hrtf.c 6 Jan 2005 13:14:30 -0000 1.5 +++ main/libaf/af_hrtf.c 10 Apr 2005 00:11:57 -0000 @@ -28,11 +28,27 @@ float *ba_ir; /* Whether to matrix decode the rear center channel */ int matrix_mode; + /* How to decode the input: + 0 = 5/5+1 channels + 1 = 2 channels + 2 = matrix encoded 2 channels */ + int decode_mode; + /* Full wave rectified (FWR) amplitudes used to steer the active + matrix decoding of front channels (variable names lpr/lmr means + Lt + Rt, Lt - Rt) */ + float l_fwr, r_fwr, lpr_fwr, lmr_fwr; + float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain; + /* Matrix input decoding require special FWR buffer, since the + decoding is done in place. */ + float *fwrbuf_l, *fwrbuf_r, *fwrbuf_lr, *fwrbuf_rr; + /* Rear channel delay buffer for matrix decoding */ + float *rear_dlbuf; /* Full wave rectified amplitude used to steer the active matrix decoding of center rear channel */ float lr_fwr, rr_fwr; /* Cyclic position on the ring buffer */ int cyc_pos; + int print_flag; } af_hrtf_t; /* Convolution on a ring buffer @@ -70,32 +86,141 @@ return 0; } -inline void update_ch(af_hrtf_t *s, short *in, const int k) +/* Unified active matrix decoder for 2 channel matrix encoded surround + sources */ +inline void matrix_decode(short *in, const int k, const int il, + const int ir, const int decode_rear, + const int dlbuflen, + float l_fwr, float r_fwr, + float lpr_fwr, float lmr_fwr, + float *adapt_l_gain, float *adapt_r_gain, + float *adapt_lpr_gain, float *adapt_lmr_gain, + float *lf, float *rf, float *lr, + float *rr, float *cf) { - /* Update the full wave rectified total amplutude */ - s->lr_fwr += abs(in[2]) - fabs(s->lr[k]); - s->rr_fwr += abs(in[3]) - fabs(s->rr[k]); - - s->lf[k] = in[0]; - s->cf[k] = in[4]; - s->rf[k] = in[1]; - s->lr[k] = in[2]; - s->rr[k] = in[3]; - - s->ba_l[k] = in[0] + in[4] + in[2]; - s->ba_r[k] = in[4] + in[1] + in[3]; + const int kr = (k + MATREARDELAY) % dlbuflen; + float l_gain = (l_fwr + r_fwr) / + (1 + l_fwr + l_fwr); + float r_gain = (l_fwr + r_fwr) / + (1 + r_fwr + r_fwr); + float lpr_gain = (lpr_fwr + lmr_fwr) / + (1 + lpr_fwr + lpr_fwr); + float lmr_gain = (lpr_fwr + lmr_fwr) / + (1 + lmr_fwr + lmr_fwr); + float lpr, lmr; + float l_agc, r_agc, lpr_agc, lmr_agc; + float f, d_gain; + +#if 0 + static int counter = 0; + static FILE *fp_out; + + if(counter == 0) + fp_out = fopen("af_hrtf.log", "w"); + fprintf(fp_out, "%g %g %g %g %g ", counter * (1.0 / 48000), + l_gain, r_gain, lpr_gain, lmr_gain); +#endif + + /*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/ + /* AGC adaption */ + d_gain = (fabs(l_gain - *adapt_l_gain) + + fabs(r_gain - *adapt_r_gain)) * 0.5; + f = d_gain * (1.0 / MATAGCTRIG); + f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); + *adapt_l_gain = (1 - f) * *adapt_l_gain + f * l_gain; + *adapt_r_gain = (1 - f) * *adapt_r_gain + f * r_gain; + /* Matrix */ + l_agc = in[il] * *adapt_l_gain; + r_agc = in[ir] * *adapt_r_gain; + cf[k] = (l_agc + r_agc) * M_SQRT1_2; + if(decode_rear) { + lr[kr] = rr[kr] = (l_agc - r_agc) * M_SQRT1_2; + /* Stereo rear channel is steered with the same AGC steering as + the decoding matrix. Note this requires a fast updating AGC + at the order of 20 ms (which is the case here). */ + lr[kr] *= (l_fwr + l_fwr) / + (1 + l_fwr + r_fwr); + rr[kr] *= (r_fwr + r_fwr) / + (1 + l_fwr + r_fwr); + } + + /*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/ + lpr = (in[il] + in[ir]) * M_SQRT1_2; + lmr = (in[il] - in[ir]) * M_SQRT1_2; + /* AGC adaption */ + d_gain = (fabs(lpr_gain - *adapt_lpr_gain) + + fabs(lmr_gain - *adapt_lmr_gain)) * 0.5; + f = d_gain * (1.0 / MATAGCTRIG); + f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); + *adapt_lpr_gain = (1 - f) * *adapt_lpr_gain + f * lpr_gain; + *adapt_lmr_gain = (1 - f) * *adapt_lmr_gain + f * lmr_gain; + /* The 2nd axis has strong gain fluctuations, and therefore require + limits. The factor is tricky. I think 2 is the reasonable + value here, which phase inverts the L, R channel if Lt, Rt is + strongly correlated (e.g. during dialogues) (1 would inhibit the + steering behavior, > 4 appears to result in distortions). */ + if(*adapt_lmr_gain > 2 * *adapt_lpr_gain) + *adapt_lmr_gain = 2 * *adapt_lpr_gain; + /* Matrix */ + lpr_agc = lpr * *adapt_lpr_gain; + lmr_agc = lmr * *adapt_lmr_gain; + lf[k] = (lpr_agc + lmr_agc) * M_SQRT1_2; + rf[k] = (lpr_agc - lmr_agc) * M_SQRT1_2; + +#if 0 + fprintf(fp_out, "%g %g %g %g\n", + *adapt_l_gain, *adapt_r_gain, + *adapt_lpr_gain, *adapt_lmr_gain); + counter++; +#endif } -inline void matrix_decode_cr(af_hrtf_t *s, short *in, const int k) +inline void update_ch(af_hrtf_t *s, short *in, const int k) { - /* Active matrix decoding of the center rear channel, 1 in the - denominator is to prevent singularity */ - float lr_agc = in[2] * (s->lr_fwr + s->rr_fwr) / - (1 + s->lr_fwr + s->lr_fwr); - float rr_agc = in[3] * (s->lr_fwr + s->rr_fwr) / - (1 + s->rr_fwr + s->rr_fwr); + const int fwr_pos = (k + FWRDURATION) % s->dlbuflen; + /* Update the full wave rectified total amplitude */ + /* Input matrix decoder */ + s->l_fwr += abs(in[0]) - fabs(s->fwrbuf_l[fwr_pos]); + s->r_fwr += abs(in[1]) - fabs(s->fwrbuf_r[fwr_pos]); + s->lpr_fwr += abs(in[0] + in[1]) - + fabs(s->fwrbuf_l[fwr_pos] + s->fwrbuf_r[fwr_pos]); + s->lmr_fwr += abs(in[0] - in[1]) - + fabs(s->fwrbuf_l[fwr_pos] - s->fwrbuf_r[fwr_pos]); + /* Rear matrix decoder */ + s->lr_fwr += abs(in[2]) - fabs(s->fwrbuf_lr[fwr_pos]); + s->rr_fwr += abs(in[3]) - fabs(s->fwrbuf_rr[fwr_pos]); + + switch (s->decode_mode) { + case HRTF_MIX_51: + /* 5/5+1 channel sources */ + s->lf[k] = in[0]; + s->cf[k] = in[4]; + s->rf[k] = in[1]; + s->fwrbuf_lr[k] = s->lr[k] = in[2]; + s->fwrbuf_rr[k] = s->rr[k] = in[3]; + break; + case HRTF_MIX_MATRIX2CH: + /* Matrix encoded 2 channel sources */ + s->fwrbuf_l[k] = in[0]; + s->fwrbuf_r[k] = in[1]; + matrix_decode(in, k, 0, 1, 1, s->dlbuflen, + s->l_fwr, s->r_fwr, + s->lpr_fwr, s->lmr_fwr, + &(s->adapt_l_gain), &(s->adapt_r_gain), + &(s->adapt_lpr_gain), &(s->adapt_lmr_gain), + s->lf, s->rf, s->lr, s->rr, s->cf); + break; + case HRTF_MIX_STEREO: + /* Stereo sources */ + s->lf[k] = in[0]; + s->rf[k] = in[1]; + s->cf[k] = s->lr[k] = s->rr[k] = 0; + break; + } - s->cr[k] = (lr_agc + rr_agc) * M_SQRT1_2; + /* We need to update the bass compensation delay line, too. */ + s->ba_l[k] = in[0] + in[4] + in[2]; + s->ba_r[k] = in[4] + in[1] + in[3]; } /* Initialization and runtime control */ @@ -118,6 +243,13 @@ af->data->nch = ((af_data_t*)arg)->nch; if(af->data->nch < 5) { af->data->nch = 5; + if(af->data->nch == 2) { + /* 2 channel input */ + if(s->decode_mode != HRTF_MIX_MATRIX2CH) { + /* Default behavior is stereo mixing. */ + s->decode_mode = HRTF_MIX_STEREO; + } + } } af->data->format = AF_FORMAT_S16_NE; af->data->bps = 2; @@ -126,14 +258,19 @@ sscanf((char*)arg, "%c", &mode); switch(mode) { case 'm': + /* Use matrix rear decoding. */ s->matrix_mode = 1; break; + case 's': + /* Input needs matrix decoding. */ + s->decode_mode = HRTF_MIX_MATRIX2CH; + break; case '0': s->matrix_mode = 0; break; default: af_msg(AF_MSG_ERROR, - "[hrtf] Mode is neither 'm', nor '0' (%c).\n", + "[hrtf] Mode is neither 'm', 's', nor '0' (%c).\n", mode); return AF_ERROR; } @@ -142,12 +279,14 @@ af_msg(AF_MSG_INFO, "[hrtf] Using HRTF to mix %s discrete surround into " - "L, R channels\n", s->matrix_mode ? "5" : "5+1"); + "L, R channels\n", s->matrix_mode ? "5+1" : "5"); if(s->matrix_mode) af_msg(AF_MSG_INFO, "[hrtf] Using active matrix to decode rear center " "channel\n"); + s->print_flag = 1; + return AF_UNKNOWN; } @@ -175,6 +314,14 @@ free(s->ba_r); if(s->ba_ir) free(s->ba_ir); + if(s->fwrbuf_l) + free(s->fwrbuf_l); + if(s->fwrbuf_r) + free(s->fwrbuf_r); + if(s->fwrbuf_lr) + free(s->fwrbuf_lr); + if(s->fwrbuf_rr) + free(s->fwrbuf_rr); free(af->setup); } if(af->data) @@ -204,6 +351,37 @@ if(AF_OK != RESIZE_LOCAL_BUFFER(af, data)) return NULL; + if(s->print_flag) { + s->print_flag = 0; + switch (s->decode_mode) { + case HRTF_MIX_51: + af_msg(AF_MSG_INFO, + "[hrtf] Using HRTF to mix %s discrete surround into " + "L, R channels\n", s->matrix_mode ? "5" : "5+1"); + break; + case HRTF_MIX_STEREO: + af_msg(AF_MSG_INFO, + "[hrtf] Using HRTF to mix stereo into " + "L, R channels\n"); + break; + case HRTF_MIX_MATRIX2CH: + af_msg(AF_MSG_INFO, + "[hrtf] Using active matrix to decode 2 channel " + "input, HRTF to mix %s matrix surround into " + "L, R channels\n", "3/2"); + break; + default: + af_msg(AF_MSG_WARN, + "[hrtf] bogus decode_mode: %d\n", s->decode_mode); + break; + } + + if(s->matrix_mode) + af_msg(AF_MSG_INFO, + "[hrtf] Using active matrix to decode rear center " + "channel\n"); + } + out = af->data->audio; /* MPlayer's 5 channel layout (notation for the variable): @@ -236,48 +414,71 @@ s->lf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen]; s->rf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen]; - /* Mixer filter matrix */ - common = conv(dblen, hlen, s->cf, s->cf_ir, k + s->cf_o); - if(s->matrix_mode) { - /* In matrix decoding mode, the rear channel gain must be - renormalized, as there is an additional channel. */ - matrix_decode_cr(s, in, k); - common += - conv(dblen, hlen, s->cr, s->cr_ir, k + s->cr_o) * - M1_76DB; - left = - ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + - conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) + - (conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) + - conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o)) * - M1_76DB + common); - right = - ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + - conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) + - (conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) + - conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o)) * - M1_76DB + common); - } - else { - left = - ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + - conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) + - conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) + - conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o) + - common); - right = - ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + - conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) + - conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) + - conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o) + - common); + switch (s->decode_mode) { + case HRTF_MIX_51: + case HRTF_MIX_MATRIX2CH: + /* Mixer filter matrix */ + common = conv(dblen, hlen, s->cf, s->cf_ir, k + s->cf_o); + if(s->matrix_mode) { + /* In matrix decoding mode, the rear channel gain must be + renormalized, as there is an additional channel. */ + matrix_decode(in, k, 2, 3, 0, s->dlbuflen, + s->l_fwr, s->r_fwr, + s->lpr_fwr, s->lmr_fwr, + &(s->adapt_l_gain), &(s->adapt_r_gain), + &(s->adapt_lpr_gain), &(s->adapt_lmr_gain), + s->lr, s->rr, NULL, NULL, s->cr); + common += + conv(dblen, hlen, s->cr, s->cr_ir, k + s->cr_o) * + M1_76DB; + left = + ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) + + (conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o)) * + M1_76DB + common); + right = + ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) + + (conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o)) * + M1_76DB + common); + } else { + left = + ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) + + conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o) + + common); + right = + ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) + + conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o) + + common); + } + break; + case HRTF_MIX_STEREO: + left = + ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o)); + right = + ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o)); + break; + default: + /* make gcc happy */ + left = 0.0; + right = 0.0; + break; } /* Bass compensation for the lower frequency cut of the HRTF. A cross talk of the left and right channel is introduced to match the directional characteristics of higher frequencies. The bass will not have any real 3D perception, but that is - OK. */ + OK (note at 180 Hz, the wavelength is about 2 m, and any + spatial perception is impossible). */ left_b = conv(dblen, blen, s->ba_l, s->ba_ir, k); right_b = conv(dblen, blen, s->ba_r, s->ba_ir, k); left += (1 - BASSCROSS) * left_b + BASSCROSS * right_b; @@ -292,12 +493,25 @@ left *= AMPLNORM; right *= AMPLNORM; - /* "Cheating": linear stereo expansion to amplify the 3D - perception. Note: Too much will destroy the acoustic space - and may even result in headaches. */ - diff = STEXPAND2 * (left - right); - out[0] = (int16_t)(left + diff); - out[1] = (int16_t)(right - diff); + switch (s->decode_mode) { + case HRTF_MIX_51: + case HRTF_MIX_STEREO: + /* "Cheating": linear stereo expansion to amplify the 3D + perception. Note: Too much will destroy the acoustic space + and may even result in headaches. */ + diff = STEXPAND2 * (left - right); + out[0] = (int16_t)(left + diff); + out[1] = (int16_t)(right - diff); + break; + case HRTF_MIX_MATRIX2CH: + /* Do attempt any stereo expansion with matrix encoded + sources. The L, R channels are already stereo expanded + by the steering, any further stereo expansion will sound + very unnatural. */ + out[0] = (int16_t)left; + out[1] = (int16_t)right; + break; + } /* The remaining channels are not needed any more */ out[2] = out[3] = out[4] = 0; @@ -330,6 +544,14 @@ if ((s->cr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; if ((s->ba_l = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; if ((s->ba_r = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_l = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_r = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_lr = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_rr = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; return 0; } @@ -357,7 +579,13 @@ s->basslen = BASSFILTLEN; s->cyc_pos = s->dlbuflen - 1; - s->matrix_mode = 1; + /* With a full (two axis) steering matrix decoder, s->matrix_mode + should not be enabled lightly (it will also steer the Ls, Rs + channels). */ + s->matrix_mode = 0; + s->decode_mode = HRTF_MIX_51; + + s->print_flag = 1; if (allocate(s) != 0) { af_msg(AF_MSG_ERROR, "[hrtf] Memory allocation error.\n"); Index: main/libaf/af_hrtf.h =================================================================== RCS file: /cvsroot/mplayer/main/libaf/af_hrtf.h,v retrieving revision 1.3 diff -u -r1.3 af_hrtf.h --- main/libaf/af_hrtf.h 5 Feb 2005 16:57:45 -0000 1.3 +++ main/libaf/af_hrtf.h 10 Apr 2005 00:11:57 -0000 @@ -1,3 +1,7 @@ +#define HRTF_MIX_51 0 +#define HRTF_MIX_STEREO 1 +#define HRTF_MIX_MATRIX2CH 2 + /* Amplitude scaling factors */ #define M17_0DB 0.1414213562 #define M6_99DB 0.4472135955 @@ -13,9 +17,15 @@ #define BASSFILTFREQ 180 /* Bass compensation filter cut (Hz) */ #define BASSFILTLEN 193 /* Bass compensation filter length */ -#define BASSGAIN M4_77DB /* Bass compensation gain */ +#define BASSGAIN M_SQRT2 /* Bass compensation gain */ #define BASSCROSS 0.35 /* Bass cross talk */ +#define FWRDURATION 240 /* FWR average duration (samples) */ +#define MATREARDELAY 720 /* Matrix mode rear delay (samples) */ + +#define MATAGCTRIG 8.0 /* (Fuzzy) AGC trigger */ +#define MATAGCDECAY 1.0 /* AGC baseline decay rate (1/samp.) */ + #define CFECHODELAY 360 /* Center front echo delay (samples) */ #define CFECHOAMPL M17_0DB /* Center front echo amplitude */