OSDN Git Service

Merge "Add memcpy_by_channel_mask and memcpy_by_index_array"
authorAndy Hung <hunga@google.com>
Wed, 2 Jul 2014 20:47:21 +0000 (20:47 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Wed, 2 Jul 2014 18:55:10 +0000 (18:55 +0000)
audio_utils/include/audio_utils/primitives.h
audio_utils/primitives.c
audio_utils/tests/build_and_run_all_unit_tests.sh [new file with mode: 0755]
audio_utils/tests/primitives_tests.cpp

index 42eb836..7bc3b87 100644 (file)
@@ -307,6 +307,76 @@ size_t nonZeroStereo32(const int32_t *frames, size_t count);
  */
 size_t nonZeroStereo16(const int16_t *frames, size_t count);
 
+/* Copy frames, selecting source samples based on a source channel mask to fit
+ * the destination channel mask. Unmatched channels in the destination channel mask
+ * are zero filled. Unmatched channels in the source channel mask are dropped.
+ * Channels present in the channel mask are represented by set bits in the
+ * uint32_t value and are matched without further interpretation.
+ * Parameters:
+ *  dst         Destination buffer
+ *  dst_mask    Bit mask corresponding to destination channels present
+ *  src         Source buffer
+ *  src_mask    Bit mask corresponding to source channels present
+ *  sample_size Size of each sample in bytes.  Must be 1, 2, 3, or 4.
+ *  count       Number of frames to copy
+ * The destination and source buffers must be completely separate (non-overlapping).
+ * If the sample size is not in range, the function will abort.
+ */
+void memcpy_by_channel_mask(void *dst, uint32_t dst_mask,
+        const void *src, uint32_t src_mask, size_t sample_size, size_t count);
+
+/* Copy frames, selecting source samples based on an index array (idxary).
+ * The idxary[] consists of dst_channels number of elements.
+ * The ith element if idxary[] corresponds the ith destination channel.
+ * A non-negative value is the channel index in the source frame.
+ * A negative index (-1) represents filling with 0.
+ *
+ * Example: Swapping L and R channels for stereo streams
+ * idxary[0] = 1;
+ * idxary[1] = 0;
+ *
+ * Example: Copying a mono source to the front center 5.1 channel
+ * idxary[0] = -1;
+ * idxary[1] = -1;
+ * idxary[2] = 0;
+ * idxary[3] = -1;
+ * idxary[4] = -1;
+ * idxary[5] = -1;
+ *
+ * This copy allows swizzling of channels or replication of channels.
+ *
+ * Parameters:
+ *  dst           Destination buffer
+ *  dst_channels  Number of destination channels per frame
+ *  src           Source buffer
+ *  src_channels  Number of source channels per frame
+ *  idxary        Array of indices representing channels in the source frame
+ *  sample_size   Size of each sample in bytes.  Must be 1, 2, 3, or 4.
+ *  count         Number of frames to copy
+ * The destination and source buffers must be completely separate (non-overlapping).
+ * If the sample size is not in range, the function will abort.
+ */
+void memcpy_by_index_array(void *dst, uint32_t dst_channels,
+        const void *src, uint32_t src_channels,
+        const int8_t *idxary, size_t sample_size, size_t count);
+
+/* Prepares an index array (idxary) from channel masks, which can be later
+ * used by memcpy_by_index_array(). Returns the number of array elements required.
+ * This may be greater than idxcount, so the return value should be checked
+ * if idxary size is less than 32. Note that idxary is a caller allocated array
+ * of at least as many channels as present in the dst_mask.
+ * Channels present in the channel mask are represented by set bits in the
+ * uint32_t value and are matched without further interpretation.
+ *
+ * Parameters:
+ *  idxary      Updated array of indices of channels in the src frame for the dst frame
+ *  idxcount    Number of caller allocated elements in idxary
+ *  dst_mask    Bit mask corresponding to destination channels present
+ *  src_mask    Bit mask corresponding to source channels present
+ */
+size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount,
+        uint32_t dst_mask, uint32_t src_mask);
+
 /**
  * Clamp (aka hard limit or clip) a signed 32-bit sample to 16-bit range.
  */
index 1fc399b..32c2a46 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cutils/bitops.h> // for popcount()
 #include <audio_utils/primitives.h>
 
 void ditherAndClamp(int32_t* out, const int32_t *sums, size_t c)
@@ -262,3 +263,151 @@ size_t nonZeroStereo16(const int16_t *frames, size_t count)
     }
     return nonZero;
 }
+
+/* struct representation of 3 bytes for packed PCM 24 bit data */
+typedef struct {uint8_t c[3];} __attribute__((__packed__)) uint8x3_t;
+
+/*
+ * C macro to do channel mask copying independent of dst/src sample type.
+ * Don't pass in any expressions for the macro arguments here.
+ */
+#define copy_frame_by_mask(dst, dmask, src, smask, count, zero) \
+{ \
+    uint32_t bit, ormask; \
+    while (count--) { \
+        ormask = dmask | smask; \
+        while (ormask) { \
+            bit = ormask & -ormask; /* get lowest bit */ \
+            ormask ^= bit; /* remove lowest bit */ \
+            if (dmask & bit) { \
+                *dst++ = smask & bit ? *src++ : zero; \
+            } else { /* source channel only */ \
+                ++src; \
+            } \
+        } \
+    } \
+}
+
+void memcpy_by_channel_mask(void *dst, uint32_t dst_mask,
+        const void *src, uint32_t src_mask, size_t sample_size, size_t count)
+{
+#if 0
+    /* alternate way of handling memcpy_by_channel_mask by using the idxary */
+    int8_t idxary[32];
+    uint32_t src_channels = popcount(src_mask);
+    uint32_t dst_channels =
+            memcpy_by_index_array_initialization(idxary, 32, dst_mask, src_mask);
+
+    memcpy_by_idxary(dst, dst_channels, src, src_channels, idxary, sample_size, count);
+#else
+    if (dst_mask == src_mask) {
+        memcpy(dst, src, sample_size * popcount(dst_mask) * count);
+        return;
+    }
+    switch (sample_size) {
+    case 1: {
+        uint8_t *udst = (uint8_t*)dst;
+        const uint8_t *usrc = (const uint8_t*)src;
+
+        copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, 0);
+    } break;
+    case 2: {
+        uint16_t *udst = (uint16_t*)dst;
+        const uint16_t *usrc = (const uint16_t*)src;
+
+        copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, 0);
+    } break;
+    case 3: { /* could be slow.  use a struct to represent 3 bytes of data. */
+        uint8x3_t *udst = (uint8x3_t*)dst;
+        const uint8x3_t *usrc = (const uint8x3_t*)src;
+        static const uint8x3_t zero; /* tricky - we use this to zero out a sample */
+
+        copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, zero);
+    } break;
+    case 4: {
+        uint32_t *udst = (uint32_t*)dst;
+        const uint32_t *usrc = (const uint32_t*)src;
+
+        copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, 0);
+    } break;
+    default:
+        abort(); /* illegal value */
+        break;
+    }
+#endif
+}
+
+/*
+ * C macro to do copying by index array, to rearrange samples
+ * within a frame.  This is independent of src/dst sample type.
+ * Don't pass in any expressions for the macro arguments here.
+ */
+#define copy_frame_by_idx(dst, dst_channels, src, src_channels, idxary, count, zero) \
+{ \
+    unsigned i; \
+    int index; \
+    while (count--) { \
+        for (i = 0; i < dst_channels; ++i) { \
+            index = idxary[i]; \
+            *dst++ = index < 0 ? zero : src[index]; \
+        } \
+        src += src_channels; \
+    } \
+}
+
+void memcpy_by_index_array(void *dst, uint32_t dst_channels,
+        const void *src, uint32_t src_channels,
+        const int8_t *idxary, size_t sample_size, size_t count)
+{
+    switch (sample_size) {
+    case 1: {
+        uint8_t *udst = (uint8_t*)dst;
+        const uint8_t *usrc = (const uint8_t*)src;
+
+        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0);
+    } break;
+    case 2: {
+        uint16_t *udst = (uint16_t*)dst;
+        const uint16_t *usrc = (const uint16_t*)src;
+
+        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0);
+    } break;
+    case 3: { /* could be slow.  use a struct to represent 3 bytes of data. */
+        uint8x3_t *udst = (uint8x3_t*)dst;
+        const uint8x3_t *usrc = (const uint8x3_t*)src;
+        static const uint8x3_t zero;
+
+        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, zero);
+    } break;
+    case 4: {
+        uint32_t *udst = (uint32_t*)dst;
+        const uint32_t *usrc = (const uint32_t*)src;
+
+        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0);
+    } break;
+    default:
+        abort(); /* illegal value */
+        break;
+    }
+}
+
+size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount,
+        uint32_t dst_mask, uint32_t src_mask)
+{
+    size_t n = 0;
+    int srcidx = 0;
+    uint32_t bit, ormask = src_mask | dst_mask;
+
+    while (ormask && n < idxcount) {
+        bit = ormask & -ormask;          /* get lowest bit */
+        ormask ^= bit;                   /* remove lowest bit */
+        if (src_mask & dst_mask & bit) { /* matching channel */
+            idxary[n++] = srcidx++;
+        } else if (src_mask & bit) {     /* source channel only */
+            ++srcidx;
+        } else {                         /* destination channel only */
+            idxary[n++] = -1;
+        }
+    }
+    return n + popcount(ormask & dst_mask);
+}
diff --git a/audio_utils/tests/build_and_run_all_unit_tests.sh b/audio_utils/tests/build_and_run_all_unit_tests.sh
new file mode 100755 (executable)
index 0000000..633e22c
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+    echo "Android build environment not set"
+    exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount
+
+echo "========================================"
+echo "testing primitives"
+adb push $OUT/system/lib/libaudioutils.so /system/lib
+adb push $OUT/system/bin/primitives_tests /system/bin
+adb shell /system/bin/primitives_tests
index 758a990..6a68915 100644 (file)
@@ -220,3 +220,155 @@ TEST(audio_utils_primitives, memcpy) {
     delete[] fary;
     delete[] pary;
 }
+
+template<typename T>
+void checkMonotoneOrZero(const T *ary, size_t size)
+{
+    T least = 0;
+
+    for (size_t i = 1; i < size; ++i) {
+        if (ary[i]) {
+            EXPECT_LT(least, ary[i]);
+            least = ary[i];
+        }
+    }
+}
+
+TEST(audio_utils_primitives, memcpy_by_channel_mask) {
+    uint32_t dst_mask;
+    uint32_t src_mask;
+    uint16_t *u16ref = new uint16_t[65536];
+    uint16_t *u16ary = new uint16_t[65536];
+
+    for (size_t i = 0; i < 65536; ++i) {
+        u16ref[i] = i;
+    }
+
+    // Test when src mask is 0.  Everything copied is zero.
+    src_mask = 0;
+    dst_mask = 0x8d;
+    memset(u16ary, 0x99, 65536 * sizeof(u16ref[0]));
+    memcpy_by_channel_mask(u16ary, dst_mask, u16ref, src_mask, sizeof(u16ref[0]),
+            65536 / popcount(dst_mask));
+    EXPECT_EQ(nonZeroMono16((int16_t*)u16ary, 65530), 0);
+
+    // Test when dst_mask is 0.  Nothing should be copied.
+    src_mask = 0;
+    dst_mask = 0;
+    memset(u16ary, 0, 65536 * sizeof(u16ref[0]));
+    memcpy_by_channel_mask(u16ary, dst_mask, u16ref, src_mask, sizeof(u16ref[0]),
+            65536);
+    EXPECT_EQ(nonZeroMono16((int16_t*)u16ary, 65530), 0);
+
+    // Test when masks are the same.  One to one copy.
+    src_mask = dst_mask = 0x8d;
+    memset(u16ary, 0x99, 65536 * sizeof(u16ref[0]));
+    memcpy_by_channel_mask(u16ary, dst_mask, u16ref, src_mask, sizeof(u16ref[0]), 555);
+    EXPECT_EQ(memcmp(u16ary, u16ref, 555 * sizeof(u16ref[0]) * popcount(dst_mask)), 0);
+
+    // Test with a gap in source:
+    // Input 3 samples, output 4 samples, one zero inserted.
+    src_mask = 0x8c;
+    dst_mask = 0x8d;
+    memset(u16ary, 0x9, 65536 * sizeof(u16ary[0]));
+    memcpy_by_channel_mask(u16ary, dst_mask, u16ref, src_mask, sizeof(u16ref[0]),
+            65536 / popcount(dst_mask));
+    checkMonotoneOrZero(u16ary, 65536);
+    EXPECT_EQ(nonZeroMono16((int16_t*)u16ary, 65536), 65536 * 3 / 4 - 1);
+
+    // Test with a gap in destination:
+    // Input 4 samples, output 3 samples, one deleted
+    src_mask = 0x8d;
+    dst_mask = 0x8c;
+    memset(u16ary, 0x9, 65536 * sizeof(u16ary[0]));
+    memcpy_by_channel_mask(u16ary, dst_mask, u16ref, src_mask, sizeof(u16ref[0]),
+            65536 / popcount(src_mask));
+    checkMonotone(u16ary, 65536 * 3 / 4);
+
+    delete[] u16ref;
+    delete[] u16ary;
+}
+
+void memcpy_by_channel_mask2(void *dst, uint32_t dst_mask,
+        const void *src, uint32_t src_mask, size_t sample_size, size_t count)
+{
+    int8_t idxary[32];
+    uint32_t src_channels = popcount(src_mask);
+    uint32_t dst_channels =
+            memcpy_by_index_array_initialization(idxary, 32, dst_mask, src_mask);
+
+    memcpy_by_index_array(dst, dst_channels, src, src_channels, idxary, sample_size, count);
+}
+
+// a modified version of the memcpy_by_channel_mask test
+// but using 24 bit type and memcpy_by_index_array()
+TEST(audio_utils_primitives, memcpy_by_index_array) {
+    uint32_t dst_mask;
+    uint32_t src_mask;
+    typedef struct {uint8_t c[3];} __attribute__((__packed__)) uint8x3_t;
+    uint8x3_t *u24ref = new uint8x3_t[65536];
+    uint8x3_t *u24ary = new uint8x3_t[65536];
+    uint16_t *u16ref = new uint16_t[65536];
+    uint16_t *u16ary = new uint16_t[65536];
+
+    EXPECT_EQ(sizeof(uint8x3_t), 3); // 3 bytes per struct
+
+    // tests prepare_index_array_from_masks()
+    EXPECT_EQ(memcpy_by_index_array_initialization(NULL, 0, 0x8d, 0x8c), 4);
+    EXPECT_EQ(memcpy_by_index_array_initialization(NULL, 0, 0x8c, 0x8d), 3);
+
+    for (size_t i = 0; i < 65536; ++i) {
+        u16ref[i] = i;
+    }
+    memcpy_to_p24_from_i16((uint8_t*)u24ref, (int16_t*)u16ref, 65536);
+
+    // Test when src mask is 0.  Everything copied is zero.
+    src_mask = 0;
+    dst_mask = 0x8d;
+    memset(u24ary, 0x99, 65536 * sizeof(u24ary[0]));
+    memcpy_by_channel_mask2(u24ary, dst_mask, u24ref, src_mask, sizeof(u24ref[0]),
+            65536 / popcount(dst_mask));
+    memcpy_to_i16_from_p24((int16_t*)u16ary, (uint8_t*)u24ary, 65536);
+    EXPECT_EQ(nonZeroMono16((int16_t*)u16ary, 65530), 0);
+
+    // Test when dst_mask is 0.  Nothing should be copied.
+    src_mask = 0;
+    dst_mask = 0;
+    memset(u24ary, 0, 65536 * sizeof(u24ary[0]));
+    memcpy_by_channel_mask2(u24ary, dst_mask, u24ref, src_mask, sizeof(u24ref[0]),
+            65536);
+    memcpy_to_i16_from_p24((int16_t*)u16ary, (uint8_t*)u24ary, 65536);
+    EXPECT_EQ(nonZeroMono16((int16_t*)u16ary, 65530), 0);
+
+    // Test when masks are the same.  One to one copy.
+    src_mask = dst_mask = 0x8d;
+    memset(u24ary, 0x99, 65536 * sizeof(u24ary[0]));
+    memcpy_by_channel_mask2(u24ary, dst_mask, u24ref, src_mask, sizeof(u24ref[0]), 555);
+    EXPECT_EQ(memcmp(u24ary, u24ref, 555 * sizeof(u24ref[0]) * popcount(dst_mask)), 0);
+
+    // Test with a gap in source:
+    // Input 3 samples, output 4 samples, one zero inserted.
+    src_mask = 0x8c;
+    dst_mask = 0x8d;
+    memset(u24ary, 0x9, 65536 * sizeof(u24ary[0]));
+    memcpy_by_channel_mask2(u24ary, dst_mask, u24ref, src_mask, sizeof(u24ref[0]),
+            65536 / popcount(dst_mask));
+    memcpy_to_i16_from_p24((int16_t*)u16ary, (uint8_t*)u24ary, 65536);
+    checkMonotoneOrZero(u16ary, 65536);
+    EXPECT_EQ(nonZeroMono16((int16_t*)u16ary, 65536), 65536 * 3 / 4 - 1);
+
+    // Test with a gap in destination:
+    // Input 4 samples, output 3 samples, one deleted
+    src_mask = 0x8d;
+    dst_mask = 0x8c;
+    memset(u24ary, 0x9, 65536 * sizeof(u24ary[0]));
+    memcpy_by_channel_mask2(u24ary, dst_mask, u24ref, src_mask, sizeof(u24ref[0]),
+            65536 / popcount(src_mask));
+    memcpy_to_i16_from_p24((int16_t*)u16ary, (uint8_t*)u24ary, 65536);
+    checkMonotone(u16ary, 65536 * 3 / 4);
+
+    delete[] u16ref;
+    delete[] u16ary;
+    delete[] u24ref;
+    delete[] u24ary;
+}