OSDN Git Service

Snap for 5706862 from e907286277a038c0bed1916c5dfcb56a15c58349 to qt-c2f2-release
[android-x86/system-media.git] / audio_utils / channels.c
index 91d18fd..6747527 100644 (file)
@@ -58,12 +58,20 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
     return out;
 }
 
+/* This is written as a C macro because it operates on generic types,
+ * which in a C++ file could be alternatively achieved by a "template"
+ * or an "auto" declaration.
+ * TODO: convert this from a C file to a C++ file.
+ */
+
 /* Channel expands (adds zeroes to audio frame end) from an input buffer to an output buffer.
  * See expand_channels() function below for parameter definitions.
  *
  * 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.
+ *
+ * Macro has a return statement.
  */
 #define EXPAND_CHANNELS(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes, zero) \
 { \
@@ -86,6 +94,84 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
     return num_out_samples * sizeof(*(out_buff)); \
 }
 
+/* Channel expands from an input buffer to an output buffer.
+ * See expand_selected_channels() function below for parameter definitions.
+ * Selected channels are replaced in the output buffer, with any extra channels
+ * per frame left alone.
+ *
+ * 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.
+ *
+ * Macro has a return statement.
+ */
+#define EXPAND_SELECTED_CHANNELS( \
+        in_buff, in_buff_chans, out_buff, out_buff_chans, 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); \
+    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_extra_chans = (out_buff_chans) - (in_buff_chans); \
+    for (src_index = 0; src_index < num_in_samples; src_index += (in_buff_chans)) { \
+        dst_ptr -= num_extra_chans; \
+        for (size_t dst_offset = num_extra_chans; dst_offset < (out_buff_chans); dst_offset++) { \
+            *dst_ptr-- = *src_ptr--; \
+        } \
+    } \
+    /* return number of *bytes* generated */ \
+    return num_out_samples * sizeof(*(out_buff)); \
+}
+
+/* Expand number of channels from an input buffer to an output buffer.
+ * See expand_channels_non_destructive() function below for parameter definitions.
+ *
+ * Input channels are copied to the output buffer, with extra output
+ * channels interleaved from back of input buffer.
+ *
+ * So for in_chans = 2, out_chans = 4: [1|2|1|2...|3|4|3|4] => [1|2|3|4|1|2|3|4...]
+ *
+ * NOTE: in_buff must be same size as out_buff and num_in_bytes must
+ * be a multiple of in_buff_channels * in_buff_sample_size.
+ *
+ * Uses a temporary buffer so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ *
+ * Macro has a return statement.
+ */
+#define EXPAND_CHANNELS_NON_DESTRUCTIVE(in_buff, in_buff_chans, \
+        out_buff, out_buff_chans, 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); \
+    typeof(out_buff) dst_ptr = (out_buff); \
+    typeof(in_buff) src_ptr = (in_buff); \
+    typeof(*out_buff) temp_buff[num_in_samples]; \
+    typeof(out_buff) temp_ptr = temp_buff; \
+    /* if in-place, copy input channels to a temp buffer */ \
+    if ((in_buff) == (out_buff)) { \
+        memcpy(temp_buff, src_ptr, (num_in_bytes)); \
+        src_ptr += num_in_samples; \
+    } else { \
+        temp_ptr = (typeof(out_buff)) src_ptr; \
+        src_ptr += num_in_samples; \
+    } \
+    /* interleave channels from end of source buffer with those from front */ \
+    size_t src_index; \
+    for (src_index = 0; src_index < num_out_samples; src_index += (out_buff_chans)) { \
+        size_t dst_offset; \
+        for (dst_offset = 0; dst_offset < (in_buff_chans); dst_offset++) { \
+            *dst_ptr++ = *temp_ptr++; \
+        } \
+        for (;dst_offset < (out_buff_chans); dst_offset++) { \
+            *dst_ptr++ = *src_ptr++; \
+        } \
+    } \
+    /* return number of *bytes* generated */ \
+    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.
@@ -95,6 +181,8 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
  * 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.
+ *
+ * Macro has a return statement.
  */
 #define EXPAND_MONO_TO_MULTI(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes, zero) \
 { \
@@ -124,6 +212,8 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
  * 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.
+ *
+ * Macro has a return statement.
  */
 #define CONTRACT_CHANNELS(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes) \
 { \
@@ -144,6 +234,56 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
     return num_out_samples * sizeof(*(out_buff)); \
 }
 
+/* Contract number of channels from an input buffer to an output buffer,
+ * storing removed channels at end of buffer.
+ *
+ * See contract_channels_non_destructive() function below for parameter definitions.
+ *
+ * So for in_chans = 4, out_chans = 2: [1|2|3|4|1|2|3|4...] => [1|2|1|2...|3|4|3|4]
+ *
+ * NOTE: in_buff must be same size as out_buff and num_in_bytes must
+ * be a multiple of in_buff_channels * in_buff_sample_size.
+ *
+ * Uses a temporary buffer so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ *
+ * Macro has a return statement.
+ */
+#define CONTRACT_CHANNELS_NON_DESTRUCTIVE(in_buff, in_buff_chans, out_buff, \
+        out_buff_chans, 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); \
+    typeof(out_buff) dst_ptr = (out_buff); \
+    typeof(in_buff) src_ptr = (in_buff); \
+    size_t num_temp_samples = num_in_samples - num_out_samples; \
+    typeof(*out_buff) temp_buff[num_temp_samples]; \
+    typeof(out_buff) temp_ptr; \
+    /* if in-place, copy input channels to a temp buffer instead of out buffer */ \
+    if ((in_buff) == (out_buff)) { \
+        temp_ptr = temp_buff; \
+    } else { \
+        temp_ptr = dst_ptr + num_out_samples; \
+    } \
+    size_t src_index; \
+    for (src_index = 0; src_index < num_in_samples; src_index += (in_buff_chans)) { \
+        size_t dst_offset; \
+        for (dst_offset = 0; dst_offset < (out_buff_chans); dst_offset++) { \
+            *dst_ptr++ = *src_ptr++; \
+        } \
+        for (;dst_offset < (in_buff_chans); dst_offset++) { \
+            *temp_ptr++ = *src_ptr++; \
+        } \
+    } \
+    /* if in-place, interleave channels from the temp buffer */ \
+    if ((in_buff) == (out_buff)) { \
+        temp_ptr = temp_buff; \
+        memcpy(dst_ptr, temp_ptr, num_temp_samples * sizeof(*(in_buff))); \
+    } \
+    /* return number of *bytes* generated */ \
+    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.
@@ -155,6 +295,8 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
  * 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.
+ *
+ * Macro has a return statement.
  */
 #define CONTRACT_TO_MONO(in_buff, out_buff, num_in_bytes) \
 { \
@@ -188,6 +330,8 @@ static inline uint8x3_t int32_to_uint8x3(int32_t in) {
  * 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.
+ *
+ * Macro has a return statement.
  */
 #define CONTRACT_TO_MONO_24(in_buff, out_buff, num_in_bytes) \
 { \
@@ -281,6 +425,53 @@ static size_t contract_channels(const void* in_buff, size_t in_buff_chans,
 
 /*
  * Convert a buffer of N-channel, interleaved samples to M-channel
+ * (where N > M).
+ *   in_buff points to the buffer of samples
+ *   in_buff_channels specifies the number of channels in the input buffer.
+ *   out_buff points to the buffer to receive converted samples.
+ *   out_buff_channels specifies the number of channels in the output buffer.
+ *   sample_size_in_bytes specifies the number of bytes per sample.
+ *   num_in_bytes size of input buffer in BYTES
+ * returns
+ *   the number of BYTES of output data.
+ * NOTE
+ *   channels > M are stored at the end of the output buffer.
+ *   The output and input buffers must be the same length.
+ *   The output and input buffers must either be completely separate (non-overlapping), or
+ *   they must both start at the same address. Partially overlapping buffers are not supported.
+ */
+static size_t contract_channels_non_destructive(const void* in_buff, size_t in_buff_chans,
+                                void* out_buff, size_t out_buff_chans,
+                                unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+    switch (sample_size_in_bytes) {
+    case 1:
+        CONTRACT_CHANNELS_NON_DESTRUCTIVE((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_NON_DESTRUCTIVE((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_NON_DESTRUCTIVE((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_NON_DESTRUCTIVE((const int32_t*)in_buff, in_buff_chans,
+                          (int32_t*)out_buff, out_buff_chans,
+                          num_in_bytes);
+        // returns in macro
+    default:
+        return 0;
+    }
+}
+
+/*
+ * Convert a buffer of N-channel, interleaved samples to M-channel
  * (where N < M).
  *   in_buff points to the buffer of samples
  *   in_buff_channels Specifies the number of channels in the input buffer.
@@ -359,6 +550,115 @@ static size_t expand_channels(const void* in_buff, size_t in_buff_chans,
     }
 }
 
+/*
+ * Convert a buffer of N-channel, interleaved samples to M-channel
+ * (where N < M).
+ *   in_buff points to the buffer of samples
+ *   in_buff_channels Specifies the number of channels in the input buffer.
+ *   out_buff points to the buffer to receive converted samples.
+ *   out_buff_channels Specifies the number of channels in the output buffer.
+ *   sample_size_in_bytes Specifies the number of bytes per sample.
+ *   num_in_bytes size of input buffer in BYTES
+ * returns
+ *   the number of BYTES of output data.
+ * NOTE
+ *   channels > N are left alone in out_buff.
+ *   The out and in buffers must either be completely separate (non-overlapping), or
+ *   they must both start at the same address. Partially overlapping buffers are not supported.
+ */
+static size_t expand_selected_channels(const void* in_buff, size_t in_buff_chans,
+                              void* out_buff, size_t out_buff_chans,
+                              unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+    switch (sample_size_in_bytes) {
+    case 1:
+
+        EXPAND_SELECTED_CHANNELS((const uint8_t*)in_buff, in_buff_chans,
+                        (uint8_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    case 2:
+
+        EXPAND_SELECTED_CHANNELS((const int16_t*)in_buff, in_buff_chans,
+                        (int16_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    case 3:
+
+        EXPAND_SELECTED_CHANNELS((const uint8x3_t*)in_buff, in_buff_chans,
+                        (uint8x3_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    case 4:
+
+        EXPAND_SELECTED_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;
+    }
+}
+
+/*
+ * Convert a buffer of N-channel, interleaved samples to M-channel
+ * (where N < M).
+ *   in_buff points to the buffer of samples
+ *   in_buff_channels Specifies the number of channels in the input buffer.
+ *   out_buff points to the buffer to receive converted samples.
+ *   out_buff_channels Specifies the number of channels in the output buffer.
+ *   sample_size_in_bytes Specifies the number of bytes per sample.
+ *   num_in_bytes size of input buffer in BYTES
+ * returns
+ *   the number of BYTES of output data.
+ * NOTE
+ *   channels > N are interleaved with data from the end of the input buffer.
+ *   The output and input buffers must be the same length.
+ *   The output and input buffers must either be completely separate (non-overlapping), or
+ *   they must both start at the same address. Partially overlapping buffers are not supported.
+ */
+static size_t expand_channels_non_destructive(const void* in_buff, size_t in_buff_chans,
+                              void* out_buff, size_t out_buff_chans,
+                              unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+    switch (sample_size_in_bytes) {
+    case 1:
+
+        EXPAND_CHANNELS_NON_DESTRUCTIVE((const uint8_t*)in_buff, in_buff_chans,
+                        (uint8_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    case 2:
+
+        EXPAND_CHANNELS_NON_DESTRUCTIVE((const int16_t*)in_buff, in_buff_chans,
+                        (int16_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    case 3:
+
+        EXPAND_CHANNELS_NON_DESTRUCTIVE((const uint8x3_t*)in_buff, in_buff_chans,
+                        (uint8x3_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    case 4:
+
+        EXPAND_CHANNELS_NON_DESTRUCTIVE((const int32_t*)in_buff, in_buff_chans,
+                        (int32_t*)out_buff, out_buff_chans,
+                        num_in_bytes);
+        // returns in macro
+
+    default:
+        return 0;
+    }
+}
+
 size_t adjust_channels(const void* in_buff, size_t in_buff_chans,
                        void* out_buff, size_t out_buff_chans,
                        unsigned sample_size_in_bytes, size_t num_in_bytes)
@@ -375,3 +675,37 @@ size_t adjust_channels(const void* in_buff, size_t in_buff_chans,
 
     return num_in_bytes;
 }
+
+size_t adjust_selected_channels(const void* in_buff, size_t in_buff_chans,
+                       void* out_buff, size_t out_buff_chans,
+                       unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+    if (out_buff_chans > in_buff_chans) {
+        return expand_selected_channels(in_buff, in_buff_chans, out_buff, out_buff_chans,
+                               sample_size_in_bytes, num_in_bytes);
+    } else if (out_buff_chans < in_buff_chans) {
+        return contract_channels(in_buff, in_buff_chans, out_buff, out_buff_chans,
+                                 sample_size_in_bytes, num_in_bytes);
+    } else if (in_buff != out_buff) {
+        memcpy(out_buff, in_buff, num_in_bytes);
+    }
+
+    return num_in_bytes;
+}
+
+size_t adjust_channels_non_destructive(const void* in_buff, size_t in_buff_chans,
+                       void* out_buff, size_t out_buff_chans,
+                       unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+    if (out_buff_chans > in_buff_chans) {
+        return expand_channels_non_destructive(in_buff, in_buff_chans, out_buff, out_buff_chans,
+                               sample_size_in_bytes, num_in_bytes);
+    } else if (out_buff_chans < in_buff_chans) {
+        return contract_channels_non_destructive(in_buff, in_buff_chans, out_buff, out_buff_chans,
+                                 sample_size_in_bytes, num_in_bytes);
+    } else if (in_buff != out_buff) {
+        memcpy(out_buff, in_buff, num_in_bytes);
+    }
+
+    return num_in_bytes;
+}