summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul McLean <pmclean@google.com>2014-09-09 09:09:16 -0700
committerPaul McLean <pmclean@google.com>2014-09-10 13:32:24 -0700
commitd110930594411564189a1123492242cb8be4fe35 (patch)
tree16182ad41a907575d6d8adb19c856a85ad1cfab9
parent03f2f0fa72ef3d5c6eb146cfcb193892d8e1b32d (diff)
downloadandroid_system_media-d110930594411564189a1123492242cb8be4fe35.tar.gz
android_system_media-d110930594411564189a1123492242cb8be4fe35.tar.bz2
android_system_media-d110930594411564189a1123492242cb8be4fe35.zip
Special-case handling of MONO to/from Multichannel conversions.
Bug 17435345 Change-Id: Ib4bb2474c8f024c5fec3e17a3dfac178f2096134
-rw-r--r--audio_utils/channels.c257
1 files changed, 225 insertions, 32 deletions
diff --git a/audio_utils/channels.c b/audio_utils/channels.c
index 2f587518..0a80edea 100644
--- a/audio_utils/channels.c
+++ b/audio_utils/channels.c
@@ -18,6 +18,46 @@
#include <audio_utils/channels.h>
#include "private/private.h"
+/*
+ * Clamps a 24-bit value from a 32-bit sample
+ */
+static inline int32_t clamp24(int32_t sample)
+{
+ if ((sample>>23) ^ (sample>>31)) {
+ sample = 0x007FFFFF ^ (sample>>31);
+ }
+ return sample;
+}
+
+/*
+ * Converts a uint8x3_t into an int32_t
+ */
+inline int32_t uint8x3_to_int32(uint8x3_t val) {
+#ifdef HAVE_BIG_ENDIAN
+ int32_t temp = (val.c[0] << 24 | val.c[1] << 16 | val.c[2] << 8) >> 8;
+#else
+ int32_t temp = (val.c[2] << 24 | val.c[1] << 16 | val.c[0] << 8) >> 8;
+#endif
+ return clamp24(temp);
+}
+
+/*
+ * Converts an int32_t to a uint8x3_t
+ */
+inline uint8x3_t int32_to_uint8x3(int32_t in) {
+ uint8x3_t out;
+#ifdef HAVE_BIG_ENDIAN
+ out.c[2] = in;
+ out.c[1] = in >> 8;
+ out.c[0] = in >> 16;
+#else
+ out.c[0] = in;
+ out.c[1] = in >> 8;
+ out.c[2] = in >> 16;
+#endif
+ return out;
+}
+
/* Channel expands (adds zeroes to audio frame end) from an input buffer to an output buffer.
* See expand_channels() function below for parameter definitions.
*
@@ -46,6 +86,38 @@
return num_out_samples * sizeof(*out_buff); \
}
+/* Channel expands from a MONO input buffer to a MULTICHANNEL output buffer by duplicating the
+ * single input channel to the first 2 output channels and 0-filling the remaining.
+ * See expand_channels() function below for parameter definitions.
+ *
+ * in_buff_chans MUST be 1 and out_buff_chans MUST be >= 2
+ *
+ * Move from back to front so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ * NOTE: num_in_bytes must be a multiple of in_buff_channels * in_buff_sample_size.
+ */
+#define EXPAND_MONO_TO_MULTI(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes, zero) \
+{ \
+ size_t num_in_samples = num_in_bytes / sizeof(*in_buff); \
+ size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; \
+ typeof(out_buff) dst_ptr = out_buff + num_out_samples - 1; \
+ size_t src_index; \
+ typeof(in_buff) src_ptr = in_buff + num_in_samples - 1; \
+ size_t num_zero_chans = out_buff_chans - in_buff_chans - 1; \
+ for (src_index = 0; src_index < num_in_samples; src_index += in_buff_chans) { \
+ size_t dst_offset; \
+ for (dst_offset = 0; dst_offset < num_zero_chans; dst_offset++) { \
+ *dst_ptr-- = zero; \
+ } \
+ for (; dst_offset < out_buff_chans; dst_offset++) { \
+ *dst_ptr-- = *src_ptr; \
+ } \
+ src_ptr--; \
+ } \
+ /* return number of *bytes* generated */ \
+ return num_out_samples * sizeof(*out_buff); \
+}
+
/* Channel contracts (removes from audio frame end) from an input buffer to an output buffer.
* See contract_channels() function below for parameter definitions.
*
@@ -72,6 +144,70 @@
return num_out_samples * sizeof(*out_buff); \
}
+/* Channel contracts from a MULTICHANNEL input buffer to a MONO output buffer by mixing the
+ * first two input channels into the single output channel (and skipping the rest).
+ * See contract_channels() function below for parameter definitions.
+ *
+ * in_buff_chans MUST be >= 2 and out_buff_chans MUST be 1
+ *
+ * Move from front to back so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ * NOTE: num_in_bytes must be a multiple of in_buff_channels * in_buff_sample_size.
+ * NOTE: Overload of the summed channels is avoided by averaging the two input channels.
+ * NOTE: Can not be used for uint8x3_t samples, see CONTRACT_TO_MONO_24() below.
+ */
+#define CONTRACT_TO_MONO(in_buff, out_buff, num_in_bytes) \
+{ \
+ size_t num_in_samples = num_in_bytes / sizeof(*in_buff); \
+ size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; \
+ size_t num_skip_samples = in_buff_chans - 2; \
+ typeof(out_buff) dst_ptr = out_buff; \
+ typeof(in_buff) src_ptr = in_buff; \
+ int32_t temp0, temp1; \
+ size_t src_index; \
+ for (src_index = 0; src_index < num_in_samples; src_index += in_buff_chans) { \
+ temp0 = *src_ptr++; \
+ temp1 = *src_ptr++; \
+ /* *dst_ptr++ = temp >> 1; */ \
+ /* This bit of magic adds and normalizes without overflow (or so claims hunga@) */ \
+ /* Bitwise half adder trick, see http://en.wikipedia.org/wiki/Adder_(electronics) */ \
+ /* Hacker's delight, p. 19 http://www.hackersdelight.org/basics2.pdf */ \
+ *dst_ptr++ = (temp0 & temp1) + ((temp0 ^ temp1) >> 1); \
+ src_ptr += num_skip_samples; \
+ } \
+ /* return number of *bytes* generated */ \
+ return num_out_samples * sizeof(*out_buff); \
+}
+
+/* Channel contracts from a MULTICHANNEL uint8x3_t input buffer to a MONO uint8x3_t output buffer
+ * by mixing the first two input channels into the single output channel (and skipping the rest).
+ * See contract_channels() function below for parameter definitions.
+ *
+ * Move from front to back so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ * NOTE: num_in_bytes must be a multiple of in_buff_channels * in_buff_sample_size.
+ * NOTE: Overload of the summed channels is avoided by averaging the two input channels.
+ * NOTE: Can not be used for normal, scalar samples, see CONTRACT_TO_MONO() above.
+ */
+#define CONTRACT_TO_MONO_24(in_buff, out_buff, num_in_bytes) \
+{ \
+ size_t num_in_samples = num_in_bytes / sizeof(*in_buff); \
+ size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; \
+ size_t num_skip_samples = in_buff_chans - 2; \
+ typeof(out_buff) dst_ptr = out_buff; \
+ typeof(in_buff) src_ptr = in_buff; \
+ int32_t temp; \
+ size_t src_index; \
+ for (src_index = 0; src_index < num_in_samples; src_index += in_buff_chans) { \
+ temp = uint8x3_to_int32(*src_ptr++); \
+ temp += uint8x3_to_int32(*src_ptr++); \
+ *dst_ptr = int32_to_uint8x3(temp >> 1); \
+ src_ptr += num_skip_samples; \
+ } \
+ /* return number of *bytes* generated */ \
+ return num_out_samples * sizeof(*out_buff); \
+}
+
/*
* Convert a buffer of N-channel, interleaved samples to M-channel
* (where N > M).
@@ -94,25 +230,50 @@ static size_t contract_channels(const void* in_buff, size_t in_buff_chans,
{
switch (sample_size_in_bytes) {
case 1:
- CONTRACT_CHANNELS((const uint8_t*)in_buff, in_buff_chans,
- (uint8_t*)out_buff, out_buff_chans,
- num_in_bytes);
- // returns in macro
+ if (out_buff_chans == 1) {
+ /* Special case Multi to Mono */
+ CONTRACT_TO_MONO((const uint8_t*)in_buff, (uint8_t*)out_buff, num_in_bytes);
+ // returns in macro
+ } else {
+ CONTRACT_CHANNELS((const uint8_t*)in_buff, in_buff_chans,
+ (uint8_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ }
case 2:
- CONTRACT_CHANNELS((const int16_t*)in_buff, in_buff_chans,
- (int16_t*)out_buff, out_buff_chans,
- num_in_bytes);
- // returns in macro
+ if (out_buff_chans == 1) {
+ /* Special case Multi to Mono */
+ CONTRACT_TO_MONO((const int16_t*)in_buff, (int16_t*)out_buff, num_in_bytes);
+ // returns in macro
+ } else {
+ CONTRACT_CHANNELS((const int16_t*)in_buff, in_buff_chans,
+ (int16_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ }
case 3:
- CONTRACT_CHANNELS((const uint8x3_t*)in_buff, in_buff_chans,
- (uint8x3_t*)out_buff, out_buff_chans,
- num_in_bytes);
- // returns in macro
+ if (out_buff_chans == 1) {
+ /* Special case Multi to Mono */
+ CONTRACT_TO_MONO_24((const uint8x3_t*)in_buff,
+ (uint8x3_t*)out_buff, num_in_bytes);
+ // returns in macro
+ } else {
+ CONTRACT_CHANNELS((const uint8x3_t*)in_buff, in_buff_chans,
+ (uint8x3_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ }
case 4:
- CONTRACT_CHANNELS((const int32_t*)in_buff, in_buff_chans,
- (int32_t*)out_buff, out_buff_chans,
- num_in_bytes);
- // returns in macro
+ if (out_buff_chans == 1) {
+ /* Special case Multi to Mono */
+ CONTRACT_TO_MONO((const int32_t*)in_buff, (int32_t*)out_buff, num_in_bytes);
+ // returns in macro
+ } else {
+ CONTRACT_CHANNELS((const int32_t*)in_buff, in_buff_chans,
+ (int32_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ }
default:
return 0;
}
@@ -142,25 +303,57 @@ static size_t expand_channels(const void* in_buff, size_t in_buff_chans,
switch (sample_size_in_bytes) {
case 1:
- EXPAND_CHANNELS((const uint8_t*)in_buff, in_buff_chans,
- (uint8_t*)out_buff, out_buff_chans,
- num_in_bytes, 0);
- // returns in macro
+ if (in_buff_chans == 1) {
+ /* special case of mono source to multi-channel */
+ EXPAND_MONO_TO_MULTI((const uint8_t*)in_buff, in_buff_chans,
+ (uint8_t*)out_buff, out_buff_chans,
+ num_in_bytes, 0);
+ // returns in macro
+ } else {
+ EXPAND_CHANNELS((const uint8_t*)in_buff, in_buff_chans,
+ (uint8_t*)out_buff, out_buff_chans,
+ num_in_bytes, 0);
+ // returns in macro
+ }
case 2:
- EXPAND_CHANNELS((const int16_t*)in_buff, in_buff_chans,
- (int16_t*)out_buff, out_buff_chans,
- num_in_bytes, 0);
- // returns in macro
+ if (in_buff_chans == 1) {
+ /* special case of mono source to multi-channel */
+ EXPAND_MONO_TO_MULTI((const int16_t*)in_buff, in_buff_chans,
+ (int16_t*)out_buff, out_buff_chans,
+ num_in_bytes, 0);
+ // returns in macro
+ } else {
+ EXPAND_CHANNELS((const int16_t*)in_buff, in_buff_chans,
+ (int16_t*)out_buff, out_buff_chans,
+ num_in_bytes, 0);
+ // returns in macro
+ }
case 3:
- EXPAND_CHANNELS((const uint8x3_t*)in_buff, in_buff_chans,
- (uint8x3_t*)out_buff, out_buff_chans,
- num_in_bytes, packed24_zero);
- // returns in macro
+ if (in_buff_chans == 1) {
+ /* special case of mono source to multi-channel */
+ EXPAND_MONO_TO_MULTI((const uint8x3_t*)in_buff, in_buff_chans,
+ (uint8x3_t*)out_buff, out_buff_chans,
+ num_in_bytes, packed24_zero);
+ // returns in macro
+ } else {
+ EXPAND_CHANNELS((const uint8x3_t*)in_buff, in_buff_chans,
+ (uint8x3_t*)out_buff, out_buff_chans,
+ num_in_bytes, packed24_zero);
+ // returns in macro
+ }
case 4:
- EXPAND_CHANNELS((const int32_t*)in_buff, in_buff_chans,
- (int32_t*)out_buff, out_buff_chans,
- num_in_bytes, 0);
- // returns in macro
+ if (in_buff_chans == 1) {
+ /* special case of mono source to multi-channel */
+ EXPAND_MONO_TO_MULTI((const int32_t*)in_buff, in_buff_chans,
+ (int32_t*)out_buff, out_buff_chans,
+ num_in_bytes, 0);
+ // returns in macro
+ } else {
+ EXPAND_CHANNELS((const int32_t*)in_buff, in_buff_chans,
+ (int32_t*)out_buff, out_buff_chans,
+ num_in_bytes, 0);
+ // returns in macro
+ }
default:
return 0;
}