summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_fmt_conv.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_fmt_conv.c')
-rw-r--r--cras/src/server/cras_fmt_conv.c100
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.