*/
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.
*/
* 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)
}
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);
+}
--- /dev/null
+#!/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
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;
+}