diff options
author | Paul McLean <pmclean@google.com> | 2014-09-12 20:01:19 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-09-12 20:01:20 +0000 |
commit | 425bf818f27d2ee92b73c8f0d7d375d88d28dd68 (patch) | |
tree | 84b6d214e27eaa10d2248aae00eb13027a2a5598 | |
parent | 8554ed50836d1d07eb03225a244937ff28600715 (diff) | |
parent | d110930594411564189a1123492242cb8be4fe35 (diff) | |
download | android_system_media-425bf818f27d2ee92b73c8f0d7d375d88d28dd68.tar.gz android_system_media-425bf818f27d2ee92b73c8f0d7d375d88d28dd68.tar.bz2 android_system_media-425bf818f27d2ee92b73c8f0d7d375d88d28dd68.zip |
Merge "Special-case handling of MONO to/from Multichannel conversions." into lmp-dev
-rw-r--r-- | audio_utils/channels.c | 257 |
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; } |