diff options
Diffstat (limited to 'cras/src/server/cras_fmt_conv.c')
-rw-r--r-- | cras/src/server/cras_fmt_conv.c | 100 |
1 files changed, 84 insertions, 16 deletions
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c index 478452d4..509db1eb 100644 --- a/cras/src/server/cras_fmt_conv.c +++ b/cras/src/server/cras_fmt_conv.c @@ -9,6 +9,7 @@ #include <syslog.h> #include <endian.h> #include <limits.h> +#include <math.h> #include "cras_fmt_conv.h" #include "cras_fmt_conv_ops.h" @@ -58,21 +59,35 @@ static int is_channel_layout_equal(const struct cras_audio_format *a, return 1; } -static void normalize_buf(float *buf, size_t size) +/* + * Calculates the normalize_factor abs_sum(ci) from given coefficients. + * Since sum(ci / abs_sum(ci)) <= 1, this could prevent sample overflow while + * upmixing or downmixing. + */ +static float normalize_factor(float *buf, size_t n) { int i; - float squre_sum = 0.0; - for (i = 0; i < size; i++) - squre_sum += buf[i] * buf[i]; + float abs_sum = 0.0; + for (i = 0; i < n; i++) + abs_sum += fabs(buf[i]); - if (squre_sum == 0.0) - return; + return 1.0 / abs_sum; +} - for (i = 0; i < size; i++) - buf[i] /= squre_sum; +/* + * Normalize all channels with the same factor to maintain + * the energy ratio between original channels. + */ +static void normalize(float **mtx, size_t m, size_t n, float factor) +{ + int i, j; + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) + mtx[i][j] *= factor; } -/* Populates the down mix matrix by rules: +/* + * Populates the down mix matrix by rules: * 1. Front/side left(right) channel will mix to left(right) of * full scale. * 2. Center and LFE will be split equally to left and right. @@ -106,9 +121,46 @@ static void surround51_to_stereo_downmix_mtx(float **mtx, mtx[STEREO_L][layout[CRAS_CH_LFE]] = 0.707; mtx[STEREO_R][layout[CRAS_CH_LFE]] = 0.707; } + normalize(mtx, 2, 6, normalize_factor(mtx[STEREO_L], 6)); +} + +/* Populates the down mix matrix by rules: + * 1. Front left(right) channel will mix to the front left(right) of + * full scale. + * 2. Rear and side left(right) channel will mix to the rear left(right) of + * full scale. + * 3. Center will be split equally to the front left and right. + * 4. LFE will be split equally to the other channels. + */ +static void surround51_to_quad_downmix_mtx(float **mtx, + int8_t layout[CRAS_CH_MAX]) +{ + if (layout[CRAS_CH_FL] != -1 && layout[CRAS_CH_FR] != -1) { + mtx[CRAS_CH_FL][layout[CRAS_CH_FL]] = 1.0; + mtx[CRAS_CH_FR][layout[CRAS_CH_FR]] = 1.0; + } + if (layout[CRAS_CH_RL] != -1 && layout[CRAS_CH_RR] != -1) { + mtx[CRAS_CH_RL][layout[CRAS_CH_RL]] = 1.0; + mtx[CRAS_CH_RR][layout[CRAS_CH_RR]] = 1.0; + } + if (layout[CRAS_CH_SL] != -1 && layout[CRAS_CH_SR] != -1) { + mtx[CRAS_CH_RL][layout[CRAS_CH_SL]] = 1.0; + mtx[CRAS_CH_RR][layout[CRAS_CH_SR]] = 1.0; + } + if (layout[CRAS_CH_FC] != -1) { + /* Split 1/2 power to the front L/R */ + mtx[CRAS_CH_FL][layout[CRAS_CH_FC]] = 0.707; + mtx[CRAS_CH_FR][layout[CRAS_CH_FC]] = 0.707; + } + if (layout[CRAS_CH_LFE] != -1) { + /* Split 1/4 power to the other channel */ + mtx[CRAS_CH_FL][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_FR][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_RL][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_RR][layout[CRAS_CH_LFE]] = 0.5; + } - normalize_buf(mtx[STEREO_L], 6); - normalize_buf(mtx[STEREO_R], 6); + normalize(mtx, 4, 6, normalize_factor(mtx[CRAS_CH_FL], 6)); } static int is_supported_format(const struct cras_audio_format *fmt) @@ -170,6 +222,12 @@ static size_t _51_to_stereo(struct cras_fmt_conv *conv, const uint8_t *in, return s16_51_to_stereo(in, in_frames, out); } +static size_t _51_to_quad(struct cras_fmt_conv *conv, const uint8_t *in, + size_t in_frames, uint8_t *out) +{ + return s16_51_to_quad(in, in_frames, out); +} + static size_t stereo_to_quad(struct cras_fmt_conv *conv, const uint8_t *in, size_t in_frames, uint8_t *out) { @@ -340,7 +398,8 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, conv->channel_converter = quad_to_stereo; } else if (in->num_channels == 2 && out->num_channels == 6) { conv->channel_converter = stereo_to_51; - } else if (in->num_channels == 6 && out->num_channels == 2) { + } else if (in->num_channels == 6 && + (out->num_channels == 2 || out->num_channels == 4)) { int in_channel_layout_set = 0; /* Checks if channel_layout is set in the incoming format */ @@ -361,11 +420,20 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, return NULL; } conv->channel_converter = convert_channels; - surround51_to_stereo_downmix_mtx( - conv->ch_conv_mtx, - conv->in_fmt.channel_layout); + if (out->num_channels == 4) { + surround51_to_quad_downmix_mtx( + conv->ch_conv_mtx, + conv->in_fmt.channel_layout); + } else { + surround51_to_stereo_downmix_mtx( + conv->ch_conv_mtx, + conv->in_fmt.channel_layout); + } } else { - conv->channel_converter = _51_to_stereo; + if (out->num_channels == 4) + conv->channel_converter = _51_to_quad; + else + conv->channel_converter = _51_to_stereo; } } else if (in->num_channels <= 8 && out->num_channels <= 8) { // For average channel counts mix from all to all. |