2014-02-04 11:37:22

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH] 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 | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 46 insertions(+), 5 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index efdf823..b5b75d3 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -39,6 +39,8 @@

#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 +222,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 +234,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 +831,19 @@ static void unregister_endpoints(void)
}
}

+static void prepare_downmix_buf(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++) {
+ int sum = ((int) input[i * 2] + input[i * 2 + 1]);
+ output[i] = sum >> 1;
+ }
+}
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
@@ -853,6 +871,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;
+ }
+
+ prepare_downmix_buf(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 +920,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 +1244,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 +1268,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 +1282,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



2014-02-04 12:44:34

by Luiz Augusto von Dentz

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

Hi Andrzej,

On Tue, Feb 4, 2014 at 1:49 PM, Szymon Janc <[email protected]> wrote:
> Hi Andrzej,
>
> On Tuesday 04 of February 2014 12:37:22 Andrzej Kaczmarek wrote:
>> 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.

Not following this one, doesn't AudioFlinger supports mono?

2014-02-04 11:49:37

by Szymon Janc

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

Hi Andrzej,

On Tuesday 04 of February 2014 12:37:22 Andrzej Kaczmarek wrote:
> 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 | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 46 insertions(+), 5 deletions(-)
>
> diff --git a/android/hal-audio.c b/android/hal-audio.c
> index efdf823..b5b75d3 100644
> --- a/android/hal-audio.c
> +++ b/android/hal-audio.c
> @@ -39,6 +39,8 @@
>
> #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 +222,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 +234,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 +831,19 @@ static void unregister_endpoints(void)
> }
> }
>
> +static void prepare_downmix_buf(struct a2dp_stream_out *out,
> + const uint8_t *buffer, size_t bytes)

I'd name it something like downmix_to_mono() or mono_downmix().

> +{
> + size_t i;
> + int16_t *input = (int16_t *) buffer;
> + int16_t *output = (int16_t *) out->downmix_buf;

This might cause unaligned memory access.

> +
> + for (i = 0; i < bytes / 2; i++) {
> + int sum = ((int) input[i * 2] + input[i * 2 + 1]);
> + output[i] = sum >> 1;

I'd just divide by 2 here.

> + }
> +}
> +
> static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
> size_t bytes)
> {
> @@ -853,6 +871,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;
> + }
> +
> + prepare_downmix_buf(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 +920,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 +1244,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 +1268,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 +1282,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;
> }
>

--
Best regards,
Szymon Janc