2014-02-04 13:16:16

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2] android/hal-audio: Add simple downmix to mono

This patch adds simple downmix support from stereo to mono in order to
support mono channel mode as it's mandatory for SBC codec. It uses
simple (L+R)/2 calculation which should be good enough.
---
android/hal-audio.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 49 insertions(+), 5 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index efdf823..cef70aa 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -36,9 +36,12 @@
#include "hal-log.h"
#include "hal-msg.h"
#include "../profiles/audio/a2dp-codecs.h"
+#include "../src/shared/util.h"

#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25

+#define FIXED_BUFFER_SIZE (20 * 512)
+
#define MAX_FRAMES_IN_PAYLOAD 15

static const uint8_t a2dp_src_uuid[] = {
@@ -220,6 +223,8 @@ struct a2dp_stream_out {
struct audio_endpoint *ep;
enum a2dp_state_t audio_state;
struct audio_input_config cfg;
+
+ uint8_t *downmix_buf;
};

struct a2dp_audio_dev {
@@ -230,7 +235,8 @@ struct a2dp_audio_dev {
static const a2dp_sbc_t sbc_presets[] = {
{
.frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
- .channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL |
+ .channel_mode = SBC_CHANNEL_MODE_MONO |
+ SBC_CHANNEL_MODE_DUAL_CHANNEL |
SBC_CHANNEL_MODE_STEREO |
SBC_CHANNEL_MODE_JOINT_STEREO,
.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
@@ -826,6 +832,21 @@ static void unregister_endpoints(void)
}
}

+static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
+ size_t bytes)
+{
+ size_t i;
+ int16_t *input = (int16_t *) buffer;
+ int16_t *output = (int16_t *) out->downmix_buf;
+
+ for (i = 0; i < bytes / 2; i++) {
+ int16_t l = le16_to_cpu(get_unaligned(&input[i * 2]));
+ int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1]));
+
+ output[i] = (l + r) / 2;
+ }
+}
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
@@ -853,6 +874,18 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
return -1;
}

+ if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+ if (!out->downmix_buf) {
+ error("audio: downmix buffer not initialized");
+ return -1;
+ }
+
+ downmix_to_mono(out, buffer, bytes);
+
+ return out->ep->codec->write_data(out->ep->codec_data,
+ out->downmix_buf, bytes / 2, out->ep->fd) * 2;
+ }
+
return out->ep->codec->write_data(out->ep->codec_data, buffer,
bytes, out->ep->fd);
}
@@ -890,16 +923,18 @@ static size_t out_get_buffer_size(const struct audio_stream *stream)
* use magic value here and out_write code takes care of splitting
* input buffer into multiple media packets.
*/
- return 20 * 512;
+ return FIXED_BUFFER_SIZE;
}

static uint32_t out_get_channels(const struct audio_stream *stream)
{
- struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
-
DBG("");

- return out->cfg.channels;
+ /* AudioFlinger can only provide stereo stream, so we return it here and
+ * later we'll downmix this to mono in case codec requires it
+ */
+
+ return AUDIO_CHANNEL_OUT_STEREO;
}

static audio_format_t out_get_format(const struct audio_stream *stream)
@@ -1212,6 +1247,12 @@ static int audio_open_output_stream(struct audio_hw_device *dev,

free(preset);

+ if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+ out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2);
+ if (!out->downmix_buf)
+ goto fail;
+ }
+
*stream_out = &out->stream;
a2dp_dev->out = out;

@@ -1230,6 +1271,7 @@ static void audio_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
struct audio_endpoint *ep = a2dp_dev->out->ep;

DBG("");
@@ -1243,6 +1285,8 @@ static void audio_close_output_stream(struct audio_hw_device *dev,
ep->codec->cleanup(ep->codec_data);
ep->codec_data = NULL;

+ free(out->downmix_buf);
+
free(stream);
a2dp_dev->out = NULL;
}
--
1.8.5.3