summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2010-04-02 15:10:48 +0000
committerjstebbins <[email protected]>2010-04-02 15:10:48 +0000
commiteb4ffde8a29cf9b77a84440fa6c404e94eb9b6a6 (patch)
treeea5149118b167b07f4a33f4f0cf9f1a8ea7ee61f /libhb
parent13eb2d6d5e7ad894d0dfe808dc3cb26a062d48b7 (diff)
downmix support for ffmpeg audio sources
now we can eat our own dogfood. i.e. aac 6ch discrete input now works, along with any other multi-channel audio ffmpeg can toss at us. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3182 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.h1
-rw-r--r--libhb/decavcodec.c109
-rw-r--r--libhb/downmix.c1348
-rw-r--r--libhb/downmix.h41
-rw-r--r--libhb/hb.c45
-rw-r--r--libhb/hbffmpeg.h1
-rw-r--r--libhb/stream.c27
-rw-r--r--libhb/work.c59
8 files changed, 1540 insertions, 91 deletions
diff --git a/libhb/common.h b/libhb/common.h
index 0c06ba967..cf1dc02d8 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -361,6 +361,7 @@ struct hb_job_s
#define HB_INPUT_CH_LAYOUT_2F2R 0x0722022
#define HB_INPUT_CH_LAYOUT_3F2R 0x0832032
#define HB_INPUT_CH_LAYOUT_4F2R 0x0942042
+#define HB_INPUT_CH_LAYOUT_3F4R 0x0a34034
#define HB_INPUT_CH_LAYOUT_HAS_LFE 0x0000100
/* define some macros to extract the various information from the HB_AMIXDOWN_XXXX values */
#define HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT( a ) ( ( a & HB_INPUT_CH_LAYOUT_DISCRETE_FRONT_MASK ) >> 16 )
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index 352c0bd1b..e083d517e 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -61,6 +61,7 @@
#include "hb.h"
#include "hbffmpeg.h"
+#include "downmix.h"
#include "libavcodec/audioconvert.h"
static int decavcodecInit( hb_work_object_t *, hb_job_t * );
@@ -108,9 +109,11 @@ struct hb_work_private_s
pts_heap_t pts_heap;
void* buffer;
struct SwsContext *sws_context; // if we have to rescale or convert color space
+ hb_downmix_t *downmix;
+ hb_sample_t *downmix_buffer;
};
-static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size );
+static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size );
static hb_buffer_t *link_buf_list( hb_work_private_t *pv );
@@ -200,6 +203,14 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
pv->context = avcodec_alloc_context();
hb_avcodec_open( pv->context, codec );
+ if ( w->audio != NULL &&
+ hb_need_downmix( w->audio->config.in.channel_layout,
+ w->audio->config.out.mixdown) )
+ {
+ pv->downmix = hb_downmix_init(w->audio->config.in.channel_layout,
+ w->audio->config.out.mixdown);
+ }
+
return 0;
}
@@ -241,6 +252,15 @@ static void decavcodecClose( hb_work_object_t * w )
av_free( pv->buffer );
pv->buffer = NULL;
}
+ if ( pv->downmix )
+ {
+ hb_downmix_close( &(pv->downmix) );
+ }
+ if ( pv->downmix_buffer )
+ {
+ free( pv->downmix_buffer );
+ pv->downmix_buffer = NULL;
+ }
free( pv );
w->private_data = NULL;
}
@@ -311,7 +331,7 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
pv->duration = 90000. /
(double)( pv->context->sample_rate * pv->context->channels );
}
- decodeAudio( pv, parser_output_buffer, parser_output_buffer_len );
+ decodeAudio( w->audio, pv, parser_output_buffer, parser_output_buffer_len );
}
}
*buf_out = link_buf_list( pv );
@@ -337,18 +357,6 @@ static int decavcodecInfo( hb_work_object_t *w, hb_work_info_t *info )
return 0;
}
-static const int chan2layout[] = {
- HB_INPUT_CH_LAYOUT_MONO, // We should allow no audio really.
- HB_INPUT_CH_LAYOUT_MONO,
- HB_INPUT_CH_LAYOUT_STEREO,
- HB_INPUT_CH_LAYOUT_2F1R,
- HB_INPUT_CH_LAYOUT_2F2R,
- HB_INPUT_CH_LAYOUT_3F2R,
- HB_INPUT_CH_LAYOUT_4F2R,
- HB_INPUT_CH_LAYOUT_STEREO,
- HB_INPUT_CH_LAYOUT_STEREO,
-};
-
static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
hb_work_info_t *info )
{
@@ -412,7 +420,8 @@ static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
info->bitrate = context->bit_rate;
info->rate = context->sample_rate;
info->rate_base = 1;
- info->channel_layout = chan2layout[context->channels & 7];
+ info->channel_layout =
+ hb_ff_layout_xlat(context->channel_layout, context->channels);
ret = 1;
break;
}
@@ -1056,6 +1065,15 @@ static int decavcodecviInit( hb_work_object_t * w, hb_job_t * job )
pv->list = hb_list_init();
pv->pts_next = -1;
pv->pts = -1;
+
+ if ( w->audio != NULL &&
+ hb_need_downmix( w->audio->config.in.channel_layout,
+ w->audio->config.out.mixdown) )
+ {
+ pv->downmix = hb_downmix_init(w->audio->config.in.channel_layout,
+ w->audio->config.out.mixdown);
+ }
+
return 0;
}
@@ -1125,7 +1143,7 @@ static int decavcodecviInfo( hb_work_object_t *w, hb_work_info_t *info )
return 0;
}
-static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
+static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size )
{
AVCodecContext *context = pv->context;
int pos = 0;
@@ -1145,6 +1163,7 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
avp.size = size - pos;
int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
+ int nsamples;
int len = avcodec_decode_audio3( context, buffer, &out_size, &avp );
if ( len <= 0 )
{
@@ -1169,8 +1188,8 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
context->sample_fmt, 1,
NULL, 0 );
// get output buffer size (in 2-byte samples) then malloc a buffer
- out_size = ( out_size * 2 ) / isamp;
- buffer = av_malloc( out_size );
+ nsamples = out_size / isamp;
+ buffer = av_malloc( nsamples * 2 );
// we're doing straight sample format conversion which behaves as if
// there were only one channel.
@@ -1179,26 +1198,56 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
const int istride[6] = { isamp };
const int ostride[6] = { 2 };
- av_audio_convert( ctx, obuf, ostride, ibuf, istride, out_size >> 1 );
+ av_audio_convert( ctx, obuf, ostride, ibuf, istride, nsamples );
av_audio_convert_free( ctx );
}
- hb_buffer_t *buf = hb_buffer_init( 2 * out_size );
+ else
+ {
+ nsamples = out_size / 2;
+ }
- // convert from bytes to total samples
- out_size >>= 1;
+ hb_buffer_t * buf;
+
+ if ( pv->downmix )
+ {
+ pv->downmix_buffer = realloc(pv->downmix_buffer, nsamples * sizeof(hb_sample_t));
+
+ int i;
+ for( i = 0; i < nsamples; ++i )
+ {
+ pv->downmix_buffer[i] = buffer[i];
+ }
+
+ int n_ch_samples = nsamples / context->channels;
+ hb_layout_remap( hb_smpte_chan_map, pv->downmix_buffer,
+ audio->config.in.channel_layout, n_ch_samples );
+
+ int channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown);
+
+ buf = hb_buffer_init( n_ch_samples * channels * sizeof(float) );
+ hb_sample_t *samples = (hb_sample_t *)buf->data;
+ hb_downmix(pv->downmix, samples, pv->downmix_buffer, n_ch_samples);
+ }
+ else
+ {
+ buf = hb_buffer_init( nsamples * sizeof(float) );
+ float *fl32 = (float *)buf->data;
+ int i;
+ for( i = 0; i < nsamples; ++i )
+ {
+ fl32[i] = buffer[i];
+ }
+ int n_ch_samples = nsamples / context->channels;
+ hb_layout_remap( hb_smpte_chan_map, fl32,
+ audio->config.in.channel_layout, n_ch_samples );
+ }
double pts = pv->pts_next;
buf->start = pts;
- pts += out_size * pv->duration;
+ pts += nsamples * pv->duration;
buf->stop = pts;
pv->pts_next = pts;
- float *fl32 = (float *)buf->data;
- int i;
- for( i = 0; i < out_size; ++i )
- {
- fl32[i] = buffer[i];
- }
hb_list_add( pv->list, buf );
// if we allocated a buffer for sample format conversion, free it
@@ -1251,7 +1300,7 @@ static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in,
pv->pts_next = in->start;
}
prepare_ffmpeg_buffer( in );
- decodeAudio( pv, in->data, in->size );
+ decodeAudio( w->audio, pv, in->data, in->size );
*buf_out = link_buf_list( pv );
return HB_WORK_OK;
diff --git a/libhb/downmix.c b/libhb/downmix.c
new file mode 100644
index 000000000..0ab9b90f3
--- /dev/null
+++ b/libhb/downmix.c
@@ -0,0 +1,1348 @@
+/* $Id: downmix.c,v 1.15 2005/03/17 19:22:47 stebbins Exp $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "common.h"
+#include "downmix.h"
+
+#define LVL_PLUS6DB 2.0
+#define LVL_PLUS3DB 1.4142135623730951
+#define LVL_3DB 0.7071067811865476
+#define LVL_45DB 0.5946035575013605
+#define LVL_6DB 0.5
+
+#define LVL_SQRT_1_3 0.577350269
+#define LVL_SQRT_2_3 0.816496581
+
+#define HB_CH_FRONT_LEFT 0x00000001
+#define HB_CH_FRONT_RIGHT 0x00000002
+#define HB_CH_FRONT_CENTER 0x00000004
+#define HB_CH_LOW_FREQUENCY 0x00000008
+#define HB_CH_BACK_LEFT 0x00000010
+#define HB_CH_BACK_RIGHT 0x00000020
+#define HB_CH_BACK_CENTER 0x00000040
+#define HB_CH_SIDE_LEFT 0x00000080
+#define HB_CH_SIDE_RIGHT 0x00000100
+
+#define HB_CH_SURROUND_MASK 0x000001f0
+#define HB_CH_MASK 0x000007ff
+
+#define HB_CH_DOLBY 0x00000800
+#define HB_CH_DPLII 0x00001000
+
+#define DOWNMIX_MONO 0
+#define DOWNMIX_STEREO 1
+#define DOWNMIX_3F 2
+#define DOWNMIX_2F1R 3
+#define DOWNMIX_3F1R 4
+#define DOWNMIX_2F2R 5
+#define DOWNMIX_3F2R 6
+#define DOWNMIX_3F4R 7
+#define DOWNMIX_DOLBY 8
+#define DOWNMIX_DPLII 9
+#define DOWNMIX_NUM_MODES 10
+
+#define DOWNMIX_CHANNEL_MASK 0x0f
+
+#define DOWNMIX_LFE_FLAG 0x10
+#define DOWNMIX_FLAGS_MASK 0x10
+
+hb_sample_t downmix_matrix[DOWNMIX_NUM_MODES][DOWNMIX_NUM_MODES][8][8] =
+{
+// MONO in
+{
+ // MONO out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DOLBY out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// STEREO in
+{
+ // MONO out
+ { { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DOLBY out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// 3F in
+{
+ // MONO out
+ { { LVL_PLUS3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DOLBY out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// 2F1R in
+{
+ // MONO out
+ { { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, LVL_3DB, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 0, 1, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 1, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, LVL_3DB, LVL_3DB, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 1 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 } },
+ // DOLBY out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// 3F1R in
+{
+ // MONO out
+ { { LVL_PLUS3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, LVL_3DB, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 1, 0, 0, 0, 0, 0, 0 , 0 },
+ { 0, 1, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 1, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, LVL_3DB, LVL_3DB, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 1 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 },
+ { 0, 0, 0, 0, 0, 0, 0 , 0 } },
+ // DOLBY out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// 2F2R in
+{
+ // MONO out
+ { { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DOLBY out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_6DB, LVL_6DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_6DB, LVL_6DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { LVL_SQRT_2_3, -LVL_SQRT_1_3, 0, 0, 0, 0, 0, 0 },
+ { -LVL_SQRT_1_3, LVL_SQRT_2_3, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// 3F2R in
+{
+ // MONO out
+ { { LVL_PLUS3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // 3F4R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DOLBY out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { LVL_SQRT_2_3, -LVL_SQRT_1_3, 0, 0, 0, 0, 0, 0 },
+ { -LVL_SQRT_1_3, LVL_SQRT_2_3, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 } },
+},
+// 3F4R in
+{
+ // MONO out
+ { { LVL_PLUS3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_6DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_6DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_6DB, 0, 0, 0, 0, 0, 0, 0 },
+ { LVL_6DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 } },
+ // STEREO out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { LVL_3DB, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 } },
+ // 3F out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 } },
+ // 2F1R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_6DB, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_6DB, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_6DB, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_6DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 } },
+ // 3F1R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_6DB, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_6DB, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_6DB, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_6DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 } },
+ // 2F2R out
+ { { 1, 1, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, LVL_3DB, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 } },
+ // 3F2R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, LVL_3DB, 0, 0, 0 },
+ { 0, 0, 0, LVL_3DB, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, LVL_3DB, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 } },
+ // 3F4R out
+ { { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1 } },
+ // DOLBY out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 } },
+ // DPLII out
+ { { LVL_3DB, LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 0, 0 },
+ { LVL_SQRT_2_3*LVL_3DB, -LVL_SQRT_1_3*LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_SQRT_1_3*LVL_3DB, LVL_SQRT_2_3*LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { LVL_SQRT_2_3*LVL_3DB, -LVL_SQRT_1_3*LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { -LVL_SQRT_1_3*LVL_3DB, LVL_SQRT_2_3*LVL_3DB, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 1, 0, 0, 0, 0, 0 } }
+},
+};
+
+static int channel_layout_map[DOWNMIX_NUM_MODES] =
+{
+ // DOWNMIX_MONO
+ (HB_CH_FRONT_CENTER),
+ // DOWNMIX_STEREO
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT),
+ // DOWNMIX_3F
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT|HB_CH_FRONT_CENTER),
+ // DOWNMIX_2F1R
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT|HB_CH_BACK_CENTER),
+ // DOWNMIX_3F1R
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT|HB_CH_FRONT_CENTER|HB_CH_BACK_CENTER),
+ // DOWNMIX_2F2R
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT|HB_CH_BACK_LEFT|HB_CH_BACK_RIGHT),
+ // DOWNMIX_3F2R
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT|HB_CH_FRONT_CENTER|HB_CH_BACK_LEFT|HB_CH_BACK_RIGHT),
+ // DOWNMIX_3F4R
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT|HB_CH_FRONT_CENTER|HB_CH_SIDE_LEFT|
+ HB_CH_SIDE_RIGHT|HB_CH_BACK_LEFT|HB_CH_BACK_RIGHT),
+ // DOWNMIX_DOLBY
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT),
+ // DOWNMIX_DPLII
+ (HB_CH_FRONT_LEFT|HB_CH_FRONT_RIGHT)
+};
+
+int hb_layout_to_mode(int layout)
+{
+ int mode;
+ switch (layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK)
+ {
+ case HB_INPUT_CH_LAYOUT_MONO:
+ mode = DOWNMIX_MONO;
+ break;
+ case HB_INPUT_CH_LAYOUT_STEREO:
+ mode = DOWNMIX_STEREO;
+ break;
+ case HB_INPUT_CH_LAYOUT_3F:
+ mode = DOWNMIX_3F;
+ break;
+ case HB_INPUT_CH_LAYOUT_2F1R:
+ mode = DOWNMIX_2F1R;
+ break;
+ case HB_INPUT_CH_LAYOUT_3F1R:
+ mode = DOWNMIX_3F1R;
+ break;
+ case HB_INPUT_CH_LAYOUT_2F2R:
+ mode = DOWNMIX_2F2R;
+ break;
+ case HB_INPUT_CH_LAYOUT_3F2R:
+ mode = DOWNMIX_3F2R;
+ break;
+ case HB_INPUT_CH_LAYOUT_4F2R:
+ mode = DOWNMIX_3F2R|DOWNMIX_LFE_FLAG;
+ break;
+ case HB_INPUT_CH_LAYOUT_3F4R:
+ mode = DOWNMIX_3F4R;
+ break;
+ case HB_INPUT_CH_LAYOUT_DOLBY:
+ mode = DOWNMIX_STEREO;
+ break;
+ default:
+ mode = DOWNMIX_STEREO;
+ break;
+ }
+ if (layout & HB_INPUT_CH_LAYOUT_DISCRETE_LFE_MASK)
+ mode |= DOWNMIX_LFE_FLAG;
+ return mode;
+}
+
+int hb_mixdown_to_mode(uint32_t mixdown)
+{
+ switch (mixdown)
+ {
+ case HB_AMIXDOWN_MONO:
+ return DOWNMIX_MONO;
+ case HB_AMIXDOWN_STEREO:
+ return DOWNMIX_STEREO;
+ case HB_AMIXDOWN_DOLBY:
+ return DOWNMIX_DOLBY;
+ case HB_AMIXDOWN_DOLBYPLII:
+ return DOWNMIX_DPLII;
+ case HB_AMIXDOWN_6CH:
+ return DOWNMIX_3F2R|DOWNMIX_LFE_FLAG;
+ default:
+ return DOWNMIX_STEREO;
+ }
+}
+
+// ffmpeg gives us SMPTE channel layout
+// We could use this layout and remap channels in encfaac,
+// but VLC may have problems with remapping, so lets
+// allow remapping to the default QuickTime order which is:
+//
+// C L R LS RS Rls Rrs LFE
+//
+// This arrangement also makes it possible to use half as
+// many downmix matrices since the matrix with and without
+// LFE are the same.
+//
+// Use hb_layout_remap to accomplish this. For convenience
+// I've provided the necessary maps.
+//
+// SMPTE channel layout
+//
+// DUAL-MONO L R
+// DUAL-MONO-LFE L R LFE
+// MONO M
+// MONO-LFE M LFE
+// STEREO L R
+// STEREO-LFE L R LFE
+// 3F L R C
+// 3F-LFE L R C LFE
+// 2F1 L R S
+// 2F1-LFE L R LFE S
+// 3F1 L R C S
+// 3F1-LFE L R C LFE S
+// 2F2 L R LS RS
+// 2F2-LFE L R LFE LS RS
+// 3F2 L R C LS RS
+// 3F2-LFE L R C LFE LS RS
+// 3F4 L R C Rls Rrs LS RS
+// 3F4-LFE L R C LFE Rls Rrs LS RS
+//
+
+// Map Indicies are mode, lfe, channel respectively
+int hb_ac3_chan_map[10][2][8] =
+{
+// w/o LFE w/ LFE
+// C L R LS RS Rls Rrs L R C LS RS Rls Rls LFE
+ {{ 0, }, { 1, 0, }}, // MONO
+ {{ 0, 1, }, { 1, 2, 0, }}, // STEREO
+ {{ 1, 0, 2, }, { 2, 1, 3, 0, }}, // 3F
+ {{ 0, 1, 2, }, { 1, 2, 3, 0, }}, // 2F1R
+ {{ 1, 0, 2, 3, }, { 2, 1, 3, 4, 0, }}, // 3F1R
+ {{ 0, 1, 2, 3, }, { 1, 2, 3, 4, 0, }}, // 2F2R
+ {{ 1, 0, 2, 3, 4, }, { 2, 1, 3, 4, 5, 0, }}, // 3F2R
+ {{ 1, 0, 2, 3, 4, 5, 6, }, { 2, 1, 3, 4, 5, 6, 7, 0 }}, // 3F4R
+ {{ 0, 1, }, { 0, 1, }}, // DOLBY
+ {{ 0, 1, }, { 0, 1, }} // DPLII
+};
+
+int hb_smpte_chan_map[10][2][8] =
+{
+// w/o LFE w/ LFE
+// L R C LS RS Rls Rrs L R C LS RS Rls Rls LFE
+ {{ 0, }, { 0, 1, }}, // MONO
+ {{ 0, 1, }, { 0, 1, 2, }}, // STEREO
+ {{ 2, 0, 1, }, { 2, 0, 1, 3, }}, // 3F
+ {{ 0, 1, 2, }, { 0, 1, 3, 2, }}, // 2F1R
+ {{ 2, 0, 1, 3, }, { 2, 0, 1, 4, 3, }}, // 3F1R
+ {{ 0, 1, 2, 3, }, { 0, 1, 3, 4, 2, }}, // 2F2R
+ {{ 2, 0, 1, 3, 4, }, { 2, 0, 1, 4, 5, 3, }}, // 3F2R
+ {{ 2, 0, 1, 5, 6, 3, 4, }, { 2, 0, 1, 6, 7, 4, 5, 3 }}, // 3F4R
+ {{ 0, 1, }, { 0, 1, }}, // DOLBY
+ {{ 0, 1, }, { 0, 1, }} // DPLII
+};
+static const uint8_t nchans_tbl[] = {1, 2, 3, 3, 4, 4, 5, 7, 2, 2};
+
+// Takes a set of samples and remaps the channel layout
+void hb_layout_remap( int (*layouts)[2][8], hb_sample_t * samples, int layout, int nsamples )
+{
+ int nchans;
+ int ii, jj;
+ int lfe;
+ int * map;
+ int mode;
+ hb_sample_t tmp[6];
+
+ mode = hb_layout_to_mode(layout);
+ lfe = ((mode & DOWNMIX_LFE_FLAG) != 0);
+ mode = mode & DOWNMIX_CHANNEL_MASK;
+ nchans = nchans_tbl[mode] + lfe;
+ map = layouts[mode][lfe];
+
+ for (ii = 0; ii < nsamples; ii++)
+ {
+ for (jj = 0; jj < nchans; jj++)
+ {
+ tmp[jj] = samples[jj];
+ }
+ for (jj = 0; jj < nchans; jj++)
+ {
+ samples[jj] = tmp[map[jj]];
+ }
+ samples += nchans;
+ }
+}
+
+static void matrix_mul(
+ hb_sample_t * dst,
+ hb_sample_t * src,
+ int nchans_out,
+ int nchans_in,
+ int nsamples,
+ hb_sample_t (*matrix)[8],
+ hb_sample_t bias)
+{
+ int nn, ii, jj;
+ hb_sample_t val;
+
+ for (nn = 0; nn < nsamples; nn++)
+ {
+ for (ii = 0; ii < nchans_out; ii++)
+ {
+ val = 0;
+ for (jj = 0; jj < nchans_in; jj++)
+ {
+ val += src[jj] * matrix[jj][ii];
+ }
+ dst[ii] = val + bias;
+ }
+ src += nchans_in;
+ dst += nchans_out;
+ }
+}
+
+static void set_level(
+ hb_sample_t (*matrix)[8],
+ hb_sample_t clev,
+ hb_sample_t slev,
+ hb_sample_t level,
+ int mode_in,
+ int mode_out)
+{
+ int ii, jj;
+ int spos;
+ int layout_in, layout_out;
+
+ for (ii = 0; ii < 8; ii++)
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ matrix[ii][jj] *= level;
+ }
+ }
+ if (mode_out >= DOWNMIX_DOLBY)
+ return;
+
+ spos = 3;
+ layout_in = channel_layout_map[mode_in];
+ layout_out = channel_layout_map[mode_out];
+
+ if (!(layout_in & HB_CH_FRONT_CENTER))
+ {
+ spos--;
+ }
+ else
+ {
+ if (!(layout_out & HB_CH_FRONT_CENTER))
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ matrix[0][jj] *= clev;
+ }
+ }
+ }
+ if (layout_in & (HB_CH_BACK_LEFT|HB_CH_BACK_RIGHT|HB_CH_BACK_CENTER|HB_CH_SIDE_LEFT|HB_CH_SIDE_RIGHT))
+ {
+ if (layout_out & (HB_CH_BACK_LEFT|HB_CH_BACK_RIGHT|HB_CH_BACK_CENTER|HB_CH_SIDE_LEFT|HB_CH_SIDE_RIGHT))
+ {
+ // Note, slev only gets set if input has surround, and output has none.
+ return;
+ }
+ }
+ if (layout_in & (HB_CH_SIDE_LEFT|HB_CH_SIDE_RIGHT))
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ matrix[spos][jj] *= slev;
+ matrix[spos+1][jj] *= slev;
+ }
+ spos += 2;
+ }
+ else if (layout_in & (HB_CH_BACK_CENTER))
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ matrix[spos][jj] *= slev;
+ }
+ }
+ if (layout_in & (HB_CH_BACK_LEFT|HB_CH_BACK_RIGHT))
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ matrix[spos][jj] *= slev;
+ matrix[spos+1][jj] *= slev;
+ }
+ }
+}
+
+#define MIXMODE(x,y) (((x)<<4)|(y))
+// The downmix operation can result in new sample values that are
+// outside the original range of sample values. If you wish to
+// guarantee that the levels to not exceed the original range,
+// call this function after initializing downmix and setting
+// your initial levels.
+//
+// Note that this can result in generally lower volume levels
+// in the resulting downmixed audio.
+void hb_downmix_adjust_level( hb_downmix_t * downmix )
+{
+ int ii, jj;
+ int mode_in, mode_out;
+ hb_sample_t level = 1.0;
+ hb_sample_t clev = downmix->clev;
+ hb_sample_t slev = downmix->slev;
+
+ mode_in = downmix->mode_in & DOWNMIX_CHANNEL_MASK;
+ mode_out = downmix->mode_out & DOWNMIX_CHANNEL_MASK;
+
+ switch MIXMODE(mode_in, mode_out)
+ {
+ case MIXMODE(DOWNMIX_STEREO, DOWNMIX_MONO):
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_2F1R):
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_3F1R):
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_3F1R):
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_3F1R):
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_3F2R):
+ level_3db:
+ level /= LVL_PLUS3DB;
+ break;
+
+ case MIXMODE(DOWNMIX_3F, DOWNMIX_MONO):
+ level /= LVL_PLUS3DB + clev * LVL_PLUS3DB;
+ break;
+
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_2F1R):
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_2F1R):
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_2F2R):
+ if (1 + clev < LVL_PLUS3DB)
+ goto level_3db;
+ case MIXMODE(DOWNMIX_3F, DOWNMIX_STEREO):
+ case MIXMODE(DOWNMIX_3F, DOWNMIX_2F1R):
+ case MIXMODE(DOWNMIX_3F, DOWNMIX_2F2R):
+ case MIXMODE(DOWNMIX_3F, DOWNMIX_DOLBY):
+ case MIXMODE(DOWNMIX_3F, DOWNMIX_DPLII):
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_2F1R):
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_2F2R):
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_2F2R):
+ level /= 1 + clev;
+ break;
+
+
+ case MIXMODE(DOWNMIX_2F1R, DOWNMIX_MONO):
+ level /= LVL_PLUS3DB + LVL_3DB * clev;
+ break;
+
+ case MIXMODE(DOWNMIX_2F1R, DOWNMIX_DOLBY):
+ level /= 1 + LVL_3DB;
+ break;
+
+ case MIXMODE(DOWNMIX_2F1R, DOWNMIX_STEREO):
+ case MIXMODE(DOWNMIX_2F1R, DOWNMIX_3F):
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_3F):
+ level /= 1 + LVL_3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_MONO):
+ level /= LVL_PLUS3DB + LVL_PLUS3DB * clev + LVL_3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_STEREO):
+ level /= 1 + clev + LVL_3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_DOLBY):
+ case MIXMODE(DOWNMIX_3F1R, DOWNMIX_DPLII):
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_DOLBY):
+ level /= 1 + LVL_PLUS3DB;
+ break;
+
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_MONO):
+ level /= LVL_PLUS3DB + LVL_PLUS3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_STEREO):
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_3F):
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_3F):
+ level /= 1 + slev;
+ break;
+
+ case MIXMODE(DOWNMIX_2F2R, DOWNMIX_DPLII):
+ level /= 1 + LVL_SQRT_1_3 + LVL_SQRT_2_3;
+ break;
+
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_MONO):
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_MONO):
+ level /= LVL_PLUS3DB + LVL_PLUS3DB * clev * LVL_PLUS3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_STEREO):
+ level /= 1 + clev + slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_DOLBY):
+ level /= 1 + 3 * LVL_3DB;
+ break;
+
+ case MIXMODE(DOWNMIX_3F2R, DOWNMIX_DPLII):
+ level /= 1 + LVL_3DB + LVL_SQRT_1_3 + LVL_SQRT_2_3;
+ break;
+
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_STEREO):
+ level /= 1 + clev + LVL_PLUS3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_3F):
+ level /= 1 + LVL_PLUS3DB * slev;
+ break;
+
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_DOLBY):
+ level /= 1 + 5 * LVL_3DB;
+ break;
+
+ case MIXMODE(DOWNMIX_3F4R, DOWNMIX_DPLII):
+ level /= 1 + LVL_3DB + 2 * LVL_SQRT_1_3 + 2 * LVL_SQRT_2_3;
+ }
+
+ for (ii = 0; ii < 8; ii++)
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ downmix->matrix[ii][jj] *= level;
+ }
+ }
+}
+
+void hb_downmix_set_bias( hb_downmix_t * downmix, hb_sample_t bias )
+{
+ downmix->bias = bias;
+}
+
+// Changes the downmix mode if it needs changing after initialization
+int hb_downmix_set_mode( hb_downmix_t * downmix, int layout, int mixdown )
+{
+ int ii, jj;
+ int lfe_in, lfe_out;
+ int mode_in, mode_out;
+ hb_sample_t (*matrix)[8];
+
+ if ( downmix == NULL )
+ return -1;
+
+ mode_in = hb_layout_to_mode(layout);
+ mode_out = hb_mixdown_to_mode(mixdown);
+ downmix->mode_in = mode_in;
+ downmix->mode_out = mode_out;
+
+ mode_in = downmix->mode_in & ~DOWNMIX_FLAGS_MASK;
+ mode_out = downmix->mode_out & ~DOWNMIX_FLAGS_MASK;
+
+ if (mode_in >= DOWNMIX_NUM_MODES || mode_out >= DOWNMIX_NUM_MODES)
+ return -1;
+
+ matrix = downmix_matrix[mode_in][mode_out];
+
+ for (ii = 0; ii < 8; ii++)
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ downmix->matrix[ii][jj] = matrix[ii][jj];
+ }
+ }
+
+ lfe_in = ((downmix->mode_in & DOWNMIX_LFE_FLAG) != 0);
+ lfe_out = ((downmix->mode_out & DOWNMIX_LFE_FLAG) != 0);
+
+ downmix->nchans_in = nchans_tbl[mode_in] + lfe_in;
+ downmix->nchans_out = nchans_tbl[mode_out] + lfe_out;
+ return 0;
+}
+
+// Changes the downmix levels if they need changing after initialization
+void hb_downmix_set_level( hb_downmix_t * downmix, hb_sample_t clev, hb_sample_t slev, hb_sample_t level )
+{
+ int ii, jj;
+ int mode_in, mode_out;
+ hb_sample_t (*matrix)[8];
+
+ if ( downmix == NULL )
+ return;
+
+ mode_in = downmix->mode_in & ~DOWNMIX_FLAGS_MASK;
+ mode_out = downmix->mode_out & ~DOWNMIX_FLAGS_MASK;
+
+ if (mode_in >= DOWNMIX_NUM_MODES || mode_out >= DOWNMIX_NUM_MODES)
+ return;
+
+ matrix = downmix_matrix[mode_in][mode_out];
+
+ for (ii = 0; ii < 8; ii++)
+ {
+ for (jj = 0; jj < 8; jj++)
+ {
+ downmix->matrix[ii][jj] = matrix[ii][jj];
+ }
+ }
+ downmix->clev = clev;
+ downmix->slev = slev;
+ downmix->level = level;
+ set_level(downmix->matrix, clev, slev, level, mode_in, mode_out);
+}
+
+hb_downmix_t * hb_downmix_init(int layout, int mixdown)
+{
+ hb_downmix_t * downmix = calloc(1, sizeof(hb_downmix_t));
+
+ if (downmix == NULL)
+ return NULL;
+ if ( hb_downmix_set_mode( downmix, layout, mixdown ) < 0 )
+ {
+ free( downmix );
+ return NULL;
+ }
+ // Set some good default values
+ downmix->clev = LVL_3DB;
+ downmix->slev = LVL_3DB;
+ downmix->level = 1.0;
+ downmix->bias = 0.0;
+ set_level(downmix->matrix, LVL_3DB, LVL_3DB, 1.0,
+ downmix->mode_in, downmix->mode_out);
+ return downmix;
+}
+
+void hb_downmix_close( hb_downmix_t **downmix )
+{
+ if (*downmix != NULL)
+ free(*downmix);
+ *downmix = NULL;
+}
+
+void hb_downmix( hb_downmix_t * downmix, hb_sample_t * dst, hb_sample_t * src, int nsamples)
+{
+ matrix_mul(dst, src, downmix->nchans_out, downmix->nchans_in,
+ nsamples, downmix->matrix, downmix->bias);
+}
+
+int hb_need_downmix( int layout, int mixdown )
+{
+ int mode_in, mode_out;
+
+ mode_in = hb_layout_to_mode(layout);
+ mode_out = hb_mixdown_to_mode(mixdown);
+
+ return (mode_in != mode_out);
+}
diff --git a/libhb/downmix.h b/libhb/downmix.h
new file mode 100644
index 000000000..f9710488b
--- /dev/null
+++ b/libhb/downmix.h
@@ -0,0 +1,41 @@
+/* $Id: downmix.h,v 1.51 2005/11/04 13:09:40 stebbins Exp $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+#ifndef DOWNMIX_H
+#define DOWNMIX_H
+
+typedef float hb_sample_t;
+
+typedef struct
+{
+ int mode_in;
+ int mode_out;
+ int nchans_in;
+ int nchans_out;
+ hb_sample_t matrix[8][8];
+ hb_sample_t clev;
+ hb_sample_t slev;
+ hb_sample_t level;
+ hb_sample_t bias;
+} hb_downmix_t;
+
+// For convenience, a map to convert smpte channel layout
+// to QuickTime channel layout.
+// Map Indicies are mode, lfe, channel respectively
+extern int hb_smpte_chan_map[10][2][8];
+extern int hb_ac3_chan_map[10][2][8];
+
+hb_downmix_t * hb_downmix_init(int layout, int mixdown);
+void hb_downmix_close( hb_downmix_t **downmix );
+int hb_downmix_set_mode( hb_downmix_t * downmix, int layout, int mixdown );
+void hb_downmix_set_level( hb_downmix_t * downmix, hb_sample_t clev, hb_sample_t slev, hb_sample_t level );
+void hb_downmix_adjust_level( hb_downmix_t * downmix );
+void hb_downmix_set_bias( hb_downmix_t * downmix, hb_sample_t bias );
+void hb_downmix( hb_downmix_t * downmix, hb_sample_t * dst, hb_sample_t * src, int nsamples);
+void hb_layout_remap( int (*layouts)[2][8], hb_sample_t * samples, int layout, int nsamples );
+int hb_need_downmix( int layout, int mixdown );
+
+#endif /* DOWNMIX_H */
diff --git a/libhb/hb.c b/libhb/hb.c
index 6f1e1ff37..8e53a7a51 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -80,6 +80,51 @@ int hb_avcodec_close(AVCodecContext *avctx)
return ret;
}
+int hb_ff_layout_xlat(int64_t ff_channel_layout, int channels)
+{
+ switch (ff_channel_layout)
+ {
+ case CH_LAYOUT_MONO:
+ return HB_INPUT_CH_LAYOUT_MONO;
+ case CH_LAYOUT_STEREO:
+ return HB_INPUT_CH_LAYOUT_STEREO;
+ case CH_LAYOUT_SURROUND:
+ return HB_INPUT_CH_LAYOUT_3F;
+ case CH_LAYOUT_4POINT0:
+ return HB_INPUT_CH_LAYOUT_3F1R;
+ case CH_LAYOUT_2_2:
+ return HB_INPUT_CH_LAYOUT_2F2R;
+ case CH_LAYOUT_QUAD:
+ return HB_INPUT_CH_LAYOUT_2F2R;
+ case CH_LAYOUT_5POINT0:
+ // ffmpeg like to neglect to signal LFE
+ if (channels == 6)
+ return HB_INPUT_CH_LAYOUT_3F2R|HB_INPUT_CH_LAYOUT_HAS_LFE;
+ return HB_INPUT_CH_LAYOUT_3F2R;
+ case CH_LAYOUT_5POINT1:
+ return HB_INPUT_CH_LAYOUT_3F2R|HB_INPUT_CH_LAYOUT_HAS_LFE;
+ case CH_LAYOUT_5POINT0_BACK:
+ // ffmpeg like to neglect to signal LFE
+ if (channels == 6)
+ return HB_INPUT_CH_LAYOUT_3F2R|HB_INPUT_CH_LAYOUT_HAS_LFE;
+ return HB_INPUT_CH_LAYOUT_3F2R;
+ case CH_LAYOUT_5POINT1_BACK:
+ return HB_INPUT_CH_LAYOUT_3F2R|HB_INPUT_CH_LAYOUT_HAS_LFE;
+ case CH_LAYOUT_7POINT0:
+ // ffmpeg like to neglect to signal LFE
+ if (channels == 8)
+ return HB_INPUT_CH_LAYOUT_3F4R|HB_INPUT_CH_LAYOUT_HAS_LFE;
+ return HB_INPUT_CH_LAYOUT_3F4R;
+ case CH_LAYOUT_7POINT1:
+ return HB_INPUT_CH_LAYOUT_3F4R|HB_INPUT_CH_LAYOUT_HAS_LFE;
+ case CH_LAYOUT_STEREO_DOWNMIX:
+ return HB_INPUT_CH_LAYOUT_STEREO;
+ default:
+ return HB_INPUT_CH_LAYOUT_STEREO;
+ }
+ return HB_INPUT_CH_LAYOUT_STEREO;
+}
+
/**
* Registers work objects, by adding the work object to a liked list.
* @param w Handle to hb_work_object_t to register.
diff --git a/libhb/hbffmpeg.h b/libhb/hbffmpeg.h
index e0a7634d3..7c17e1a64 100644
--- a/libhb/hbffmpeg.h
+++ b/libhb/hbffmpeg.h
@@ -9,3 +9,4 @@
void hb_avcodec_init(void);
int hb_avcodec_open( AVCodecContext *, struct AVCodec * );
int hb_avcodec_close( AVCodecContext * );
+int hb_ff_layout_xlat(int64_t ff_layout, int channels);
diff --git a/libhb/stream.c b/libhb/stream.c
index 9d0ce8071..f75400f45 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -1435,6 +1435,18 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
sizeof( audio->config.lang.description ), "%s (%s)",
strlen(lang->native_name) ? lang->native_name : lang->eng_name,
codec_name );
+
+ if (audio->config.in.codec == HB_ACODEC_FFMPEG)
+ {
+ int layout = audio->config.in.channel_layout;
+ char *desc = audio->config.lang.description +
+ strlen( audio->config.lang.description );
+ sprintf( desc, " (%d.%d ch)",
+ HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(layout) +
+ HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(layout),
+ HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(layout) );
+ }
+
snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s",
strlen(lang->native_name) ? lang->native_name : lang->eng_name );
snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ),
@@ -2777,18 +2789,6 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
// paramters here.
if ( codec->bit_rate || codec->sample_rate )
{
- static const int chan2layout[] = {
- HB_INPUT_CH_LAYOUT_MONO, // We should allow no audio really.
- HB_INPUT_CH_LAYOUT_MONO,
- HB_INPUT_CH_LAYOUT_STEREO,
- HB_INPUT_CH_LAYOUT_2F1R,
- HB_INPUT_CH_LAYOUT_2F2R,
- HB_INPUT_CH_LAYOUT_3F2R,
- HB_INPUT_CH_LAYOUT_4F2R,
- HB_INPUT_CH_LAYOUT_STEREO,
- HB_INPUT_CH_LAYOUT_STEREO,
- };
-
hb_audio_t *audio = calloc( 1, sizeof(*audio) );;
audio->id = id;
@@ -2807,7 +2807,8 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
audio->config.in.bitrate = codec->bit_rate? codec->bit_rate : 1;
audio->config.in.samplerate = codec->sample_rate;
- audio->config.in.channel_layout = chan2layout[codec->channels & 7];
+ audio->config.in.channel_layout =
+ hb_ff_layout_xlat(codec->channel_layout, codec->channels);
}
set_audio_description( audio, lang_for_code2( st->language ) );
diff --git a/libhb/work.c b/libhb/work.c
index ac672f5f6..af4665bbc 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -546,7 +546,6 @@ static void do_job( hb_job_t * job, int cpu_count )
// So if the encoder is lame we need the output to be stereo (or multichannel
// matrixed into stereo like dpl). If the decoder is not AC3 or DCA the
// encoder has to handle the input format since we can't do a mixdown.
-#define CAN_MIXDOWN(a) ( a->config.in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA) )
#define STEREO_ONLY(a) ( a->config.out.codec & HB_ACODEC_LAME )
switch (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK)
@@ -566,16 +565,6 @@ static void do_job( hb_job_t * job, int cpu_count )
case HB_INPUT_CH_LAYOUT_MONO:
if ( STEREO_ONLY( audio ) )
{
- if ( !CAN_MIXDOWN( audio ) )
- {
- // XXX we're hosed - we can't mix up & lame can't handle
- // the input format. The user shouldn't be able to make
- // this choice. It's too late to do anything about it now
- // so complain in the log & let things abort in lame.
- hb_log( "ERROR - can't use lame mp3 audio output with "
- "mono audio stream %x - output will be messed up",
- audio->id );
- }
audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
}
else
@@ -589,7 +578,7 @@ static void do_job( hb_job_t * job, int cpu_count )
// the A52 flags don't allow for a way to distinguish between DPL1 and
// DPL2 on a DVD so we always assume a DPL1 source for A52_DOLBY.
case HB_INPUT_CH_LAYOUT_DOLBY:
- if ( STEREO_ONLY( audio ) || !CAN_MIXDOWN( audio ) ||
+ if ( STEREO_ONLY( audio ) ||
audio->config.out.mixdown > HB_AMIXDOWN_DOLBY )
{
audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
@@ -599,57 +588,31 @@ static void do_job( hb_job_t * job, int cpu_count )
// 4 channel discrete
case HB_INPUT_CH_LAYOUT_2F2R:
case HB_INPUT_CH_LAYOUT_3F1R:
- if ( CAN_MIXDOWN( audio ) )
- {
- if ( STEREO_ONLY( audio ) ||
- audio->config.out.mixdown > HB_AMIXDOWN_DOLBY )
- {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
- }
- }
- else
+ if ( STEREO_ONLY( audio ) ||
+ audio->config.out.mixdown > HB_AMIXDOWN_DOLBY )
{
- // XXX we can't mixdown & don't have any way to specify
- // 4 channel discrete output so we're hosed.
- hb_log( "ERROR - can't handle 4 channel discrete audio stream "
- "%x - output will be messed up", audio->id );
+ audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
}
break;
// 5 or 6 channel discrete
case HB_INPUT_CH_LAYOUT_3F2R:
- if ( CAN_MIXDOWN( audio ) )
+ if ( STEREO_ONLY( audio ) )
{
- if ( STEREO_ONLY( audio ) )
+ if ( audio->config.out.mixdown < HB_AMIXDOWN_STEREO )
{
- if ( audio->config.out.mixdown < HB_AMIXDOWN_STEREO )
- {
- audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
- }
- else if ( audio->config.out.mixdown > HB_AMIXDOWN_DOLBYPLII )
- {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
- }
+ audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
}
- else if ( ! ( audio->config.in.channel_layout &
- HB_INPUT_CH_LAYOUT_HAS_LFE ) )
+ else if ( audio->config.out.mixdown > HB_AMIXDOWN_DOLBYPLII )
{
- // we don't do 5 channel discrete so mixdown to DPLII
audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
}
}
else if ( ! ( audio->config.in.channel_layout &
- HB_INPUT_CH_LAYOUT_HAS_LFE ) )
- {
- // XXX we can't mixdown & don't have any way to specify
- // 5 channel discrete output so we're hosed.
- hb_log( "ERROR - can't handle 5 channel discrete audio stream "
- "%x - output will be messed up", audio->id );
- }
- else
+ HB_INPUT_CH_LAYOUT_HAS_LFE ) )
{
- // we can't mixdown so force 6 channel discrete
- audio->config.out.mixdown = HB_AMIXDOWN_6CH;
+ // we don't do 5 channel discrete so mixdown to DPLII
+ audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
}
break;
}