diff options
author | jstebbins <[email protected]> | 2010-04-02 15:10:48 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2010-04-02 15:10:48 +0000 |
commit | eb4ffde8a29cf9b77a84440fa6c404e94eb9b6a6 (patch) | |
tree | ea5149118b167b07f4a33f4f0cf9f1a8ea7ee61f | |
parent | 13eb2d6d5e7ad894d0dfe808dc3cb26a062d48b7 (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
-rw-r--r-- | gtk/src/hb-backend.c | 13 | ||||
-rw-r--r-- | libhb/common.h | 1 | ||||
-rw-r--r-- | libhb/decavcodec.c | 109 | ||||
-rw-r--r-- | libhb/downmix.c | 1348 | ||||
-rw-r--r-- | libhb/downmix.h | 41 | ||||
-rw-r--r-- | libhb/hb.c | 45 | ||||
-rw-r--r-- | libhb/hbffmpeg.h | 1 | ||||
-rw-r--r-- | libhb/stream.c | 27 | ||||
-rw-r--r-- | libhb/work.c | 59 |
9 files changed, 1542 insertions, 102 deletions
diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index 53b71fd2d..dbcb437a9 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -1397,8 +1397,6 @@ ghb_grey_combo_options(GtkBuilder *builder) allow_6ch = acodec & ~HB_ACODEC_LAME; if (audio) { - allow_mono = allow_mono && - (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)); gint layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; allow_stereo = ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO); @@ -1408,7 +1406,6 @@ ghb_grey_combo_options(GtkBuilder *builder) (layout == HB_INPUT_CH_LAYOUT_DOLBY); allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R); allow_6ch = allow_6ch && - (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && (layout == HB_INPUT_CH_LAYOUT_3F2R) && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE); } @@ -1449,9 +1446,7 @@ ghb_get_best_mix(gint titleindex, gint track, gint acodec, gint mix) audio = get_hb_audio(titleindex, track); if (audio) { - allow_mono = - (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && - (acodec & ~HB_ACODEC_LAME); + allow_mono = (acodec & ~HB_ACODEC_LAME); gint layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; allow_stereo = ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO); @@ -1461,7 +1456,6 @@ ghb_get_best_mix(gint titleindex, gint track, gint acodec, gint mix) (layout == HB_INPUT_CH_LAYOUT_DOLBY); allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R); allow_6ch = - (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && (acodec & ~HB_ACODEC_LAME) && (layout == HB_INPUT_CH_LAYOUT_3F2R) && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE); @@ -4096,9 +4090,7 @@ ghb_validate_audio(signal_user_data_t *ud) gboolean allow_dolby = TRUE; gboolean allow_dpl2 = TRUE; gboolean allow_6ch = TRUE; - allow_mono = - (taudio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && - (codec & ~HB_ACODEC_LAME); + allow_mono = (codec & ~HB_ACODEC_LAME); gint layout = taudio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; allow_stereo = ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO); @@ -4108,7 +4100,6 @@ ghb_validate_audio(signal_user_data_t *ud) (layout == HB_INPUT_CH_LAYOUT_DOLBY); allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R); allow_6ch = - (taudio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && (codec & ~HB_ACODEC_LAME) && (layout == HB_INPUT_CH_LAYOUT_3F2R) && (taudio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE); 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; } |